Building and packaging applications with Flox. Use for manifest builds, Nix expression builds, sandbox modes, multi-stage builds, and packaging assets.
84
81%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Flox supports two build modes, each with its own strengths:
Manifest builds enable you to define your build steps in your manifest and reuse your existing build scripts and toolchains. Flox manifests are declarative artifacts, expressed in TOML.
Manifest builds:
sandbox = "pure") for reproducible buildsNix expression builds guarantee build-time reproducibility because they're both isolated and purely functional. Their learning curve is steeper because they require proficiency with the Nix language.
Nix expression builds:
You can mix both approaches in the same project, but package names must be unique.
flox build # Build all targets
flox build app docs # Build specific targets
flox build -d /path/to/project # Build in another directory
flox build -v # Verbose output
flox build .#hello # Build specific Nix expressionA common workflow involves two separate environments:
Contains source code, build tools, and build definitions:
# project-dev/.flox/env/manifest.toml (in git with source code)
[install]
gcc.pkg-path = "gcc13"
make.pkg-path = "make"
python.pkg-path = "python311Full"
uv.pkg-path = "uv"
[build.myapp]
command = '''
make build
mkdir -p $out/bin
cp build/myapp $out/bin/
'''
version = "1.0.0"Workflow:
cd project-dev
flox activate
flox build myapp
flox publish -o myorg myappContains only the published package and runtime dependencies:
# project-runtime/.flox/env/manifest.toml (can push to FloxHub)
[install]
myapp.pkg-path = "myorg/myapp" # The published packageWorkflow:
cd project-runtime
flox init
flox install myorg/myapp
flox push # Share runtime environment without source codeWhy separate environments?
Note: You can also install published packages into existing environments (other projects, production environments, etc.), not just dedicated runtime environments.
Flox treats a manifest build as a short, deterministic Bash script that runs inside an activated environment and copies its deliverables into $out. Anything copied there becomes a first-class, versioned package that can later be published and installed like any other catalog artifact.
[hook] scripts DO NOT execute during flox build - only during interactive flox activate${FLOX_ENV_CACHE:-} with default fallback in hooks to avoid build failures$out/bin/ that set up runtime environment:
cat > "$out/bin/myapp" << 'EOF'
#!/usr/bin/env bash
APP_ROOT="$(dirname "$(dirname "$(readlink -f "$0")")")"
export PYTHONPATH="$APP_ROOT/share/myapp:$PYTHONPATH"
exec python3 "$APP_ROOT/share/myapp/main.py" "$@"
EOF
chmod +x "$out/bin/myapp"~/.myapp/ for user configs, not $FLOX_ENV_CACHE (packages are immutable)mkdir -p "${MYAPP_DIR:-$HOME/.myapp}/models"requirements.txt and setup script:
# In build, create setup script:
cat > "$out/bin/myapp-setup" << 'EOF'
venv="${VENV:-$HOME/.myapp/venv}"
uv venv "$venv" --python python3
uv pip install --python "$venv/bin/python" -r "$APP_ROOT/share/myapp/requirements.txt"
EOFproject-dev/), another for consuming (project-runtime/). See "Development vs Runtime: The Two-Environment Pattern" section above for details.[build.<name>]
command = ''' # required – Bash, multiline string
<your build steps> # e.g. cargo build, npm run build
mkdir -p $out/bin
cp path/to/artifact $out/bin/<name>
'''
version = "1.2.3" # optional
description = "one-line summary" # optional
sandbox = "pure" | "off" # default: off
runtime-packages = [ "id1", "id2" ] # optionalOne table per package. Multiple [build.*] tables let you publish, for example, a stripped release binary and a debug build from the same sources.
Bash only. The script executes under set -euo pipefail. If you need zsh or fish features, invoke them explicitly inside the script.
Environment parity. Before your script runs, Flox performs the equivalent of flox activate — so every tool listed in [install] is on PATH.
Package groups and builds. Only packages in the toplevel group (default) are available during builds. Packages with explicit pkg-group settings won't be accessible in build commands unless also installed to toplevel.
Referencing other builds. ${other} expands to the $out of [build.other] and forces that build to run first, enabling multi-stage flows (e.g. vendoring → compilation).
| sandbox value | Filesystem scope | Network | Typical use-case |
|---|---|---|---|
"off" (default) | Project working tree; complete host FS | allowed | Fast, iterative dev builds |
"pure" | Git-tracked files only, copied to tmp | Linux: blocked<br>macOS: allowed | Reproducible, host-agnostic packages |
Pure mode highlights undeclared inputs early and is mandatory for builds intended for CI/CD publication. When a pure build needs pre-fetched artifacts (e.g. language modules) use a two-stage pattern:
[build.deps]
command = '''go mod vendor -o $out/etc/vendor'''
sandbox = "off"
[build.app]
command = '''
cp -r ${deps}/etc/vendor ./vendor
go build ./...
mkdir -p $out/bin
cp app $out/bin/
'''
sandbox = "pure"Only files placed under $out survive. Follow FHS conventions:
| Path | Purpose |
|---|---|
$out/bin / $out/sbin | CLI and daemon binaries (must be chmod +x) |
$out/lib, $out/libexec | Shared libraries, helper programs |
$out/share/man | Man pages (gzip them) |
$out/etc | Configuration shipped with the package |
Scripts or binaries stored elsewhere will not end up on callers' paths.
# Build every target in the manifest
flox build
# Build a subset
flox build app docs
# Build a manifest in another directory
flox build -d /path/to/projectResults appear as immutable symlinks: ./result-<name> → /nix/store/...-<name>-<version>.
To execute a freshly built binary: ./result-app/bin/app.
[build.bin]
command = '''
cargo build --release
mkdir -p $out/bin
cp target/release/myproject $out/bin/
'''
version = "0.9.0"
[build.src]
command = '''
git archive --format=tar HEAD | gzip > $out/myproject-${bin.version}.tar.gz
'''
sandbox = "pure"${bin.version} resolves because both builds share the same manifest.
[build.vendor]
command = '''
go mod vendor
mkdir -p $out/vendor
cp -r vendor/* $out/vendor/
'''
sandbox = "off"
[build.app]
command = '''
cp -r ${vendor}/vendor ./
go build -mod=vendor -o $out/bin/myapp
'''
sandbox = "pure"By default, every package in the toplevel install-group becomes a runtime dependency of your build's closure—even if it was only needed at compile time.
Declare a minimal list instead:
[install]
clang.pkg-path = "clang"
pytest.pkg-path = "pytest"
[build.cli]
command = '''
make
mv build/cli $out/bin/
'''
runtime-packages = [ "clang" ] # exclude pytest from runtime closureSmaller closures copy faster and occupy less disk when installed on users' systems.
Flox surfaces these fields in flox search, flox show, and during publication.
[build.mytool]
version.command = "git describe --tags"
description = "High-performance log shipper"Alternative forms:
version = "1.4.2" # static string
version.file = "VERSION.txt" # read at build timeflox build targets the host's systems triple. To ship binaries for additional platforms you must trigger the build on machines (or CI runners) of those architectures:
linux-x86_64 → build → publish
darwin-aarch64 → build → publishThe manifest can remain identical across hosts.
Any artifact that can be copied into $out can be versioned and installed:
[build.nginx_cfg]
command = '''mkdir -p $out/etc && cp nginx.conf $out/etc/'''[build.proto]
command = '''
mkdir -p $out/share/proto
cp proto/**/*.proto $out/share/proto/
'''Teams install these packages and reference them via $FLOX_ENV/etc/nginx.conf or $FLOX_ENV/share/proto.
You can write a Nix expression instead of (or in addition to) defining a manifest build.
Put *.nix build files in .flox/pkgs/ for Nix expression builds. Git add all files before building.
hello.nix → package named hellohello/default.nix → package named helloShell Script
{writeShellApplication, curl}:
writeShellApplication {
name = "my-ip";
runtimeInputs = [ curl ];
text = ''curl icanhazip.com'';
}Your Project
{ rustPlatform, lib }:
rustPlatform.buildRustPackage {
pname = "my-app";
version = "0.1.0";
src = ../../.;
cargoLock.lockFile = "${src}/Cargo.lock";
}Update Version
{ hello, fetchurl }:
hello.overrideAttrs (finalAttrs: _: {
version = "2.12.2";
src = fetchurl {
url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz";
hash = "sha256-WpqZbcKSzCTc9BHO6H6S9qrluNE72caBm0x6nc4IGKs=";
};
})Apply Patches
{ hello }:
hello.overrideAttrs (oldAttrs: {
patches = (oldAttrs.patches or []) ++ [ ./my.patch ];
})hash = "";flox buildflox build - build allflox build .#hello - build specificgit add .flox/pkgs/* - track files[build.myapp]
command = '''
mkdir -p $out/bin $out/share/myapp
# Copy application code
cp -r src/* $out/share/myapp/
cp requirements.txt $out/share/myapp/
# Create wrapper script
cat > $out/bin/myapp << 'EOF'
#!/usr/bin/env bash
APP_ROOT="$(dirname "$(dirname "$(readlink -f "$0")")")"
export PYTHONPATH="$APP_ROOT/share/myapp:$PYTHONPATH"
exec python3 "$APP_ROOT/share/myapp/main.py" "$@"
EOF
chmod +x $out/bin/myapp
'''
version = "1.0.0"[build.webapp]
command = '''
npm ci
npm run build
mkdir -p $out/share/webapp
cp -r dist/* $out/share/webapp/
cp package.json package-lock.json $out/share/webapp/
cd $out/share/webapp && npm ci --production
'''
version = "1.0.0"[build.cli]
command = '''
cargo build --release
mkdir -p $out/bin
cp target/release/mycli $out/bin/
'''
version.command = "cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version'"Build hooks don't run: [hook] scripts DO NOT execute during flox build
Package groups: Only toplevel group packages available during builds
Network access: Pure builds can't access network on Linux
flox build -vls -la result-<name>/./result-<name>/bin/<name>nix-store -q --references result-<name>32d3e1e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.