Skip to main content
Build backends decouple package building from Pixi, providing language-specific build implementations through a standardized protocol.

What Are Build Backends?

Build backends are executables that:
  • Follow a specific protocol for communication with Pixi
  • Handle language-specific build logic
  • Convert projects into conda packages
  • Are decoupled from Pixi’s manifest specification
This design allows backends to evolve independently from Pixi itself.

Available Backends

pixi-build-cmake

For CMake-based C/C++ projects. Automatically provides CMake, Ninja, and compilers.

pixi-build-python

For Python packages using PEP 517 build backends like Hatchling or setuptools.

pixi-build-rattler-build

For direct recipe.yaml builds with full control over the build process.

pixi-build-ros

For ROS (Robot Operating System) packages using colcon.

pixi-build-rust

For Cargo-based Rust applications and libraries.

pixi-build-mojo

For Mojo applications and packages.
All backends are available through conda-forge and work across Linux, macOS, and Windows.

Quick Comparison

BackendUse CaseAuto-installed ToolsConfiguration
cmakeC/C++ projectsCMake, Ninja, compilersCMakeLists.txt
pythonPython packagesNonepyproject.toml + pixi.toml
rattler-buildCustom recipesNonerecipe.yaml
rosROS packagescolcon, rosdeppackage.xml
rustRust projectsRust toolchainCargo.toml
mojoMojo projectsMojo toolchainmagic.toml

Installing a Backend

Specify the backend in your manifest:
pixi.toml
[package.build]
backend = { name = "pixi-build-python", version = "0.4.*" }
Pixi automatically installs the backend from conda channels.

Using Custom Backend Channels

For the latest features, use the dedicated backend channel:
pixi.toml
[package.build.backend]
channels = [
  "https://prefix.dev/pixi-build-backends",
  "https://prefix.dev/conda-forge",
]
name = "pixi-build-python"
version = "0.4.*"
The pixi-build-backends channel contains pre-release versions with the latest features.

Common Backend Configurations

[package.build]
backend = { name = "pixi-build-python", version = "0.4.*" }

[package.host-dependencies]
hatchling = "*"

[package.run-dependencies]
rich = ">=13.9"

Backend-Specific Configuration

Each backend supports custom configuration through [package.build.config]:

CMake Backend

[package.build.config]
extra-args = [
  "-DCMAKE_BUILD_TYPE=Release",
  "-DUSE_OPENMP=ON",
  "-DBUILD_TESTING=OFF"
]

Python Backend

[package.build.config]
noarch = true      # Platform-independent package
editable = true    # Editable install
skip-install = false

Rust Backend

[package.build.config]
features = ["full", "tokio"]
release = true
See individual backend documentation for complete options.

Overriding Build Backends

For development, you can override backends:

Override Specific Backends

export PIXI_BUILD_BACKEND_OVERRIDE="pixi-build-cmake=/path/to/bin,pixi-build-python"
pixi build
Format: {name}={path} with multiple backends separated by commas.
  • pixi-build-cmake=/path/to/bin - Use local executable
  • pixi-build-python - Use version from PATH

Override All Backends

export PIXI_BUILD_BACKEND_OVERRIDE_ALL=1
pixi build
This assumes all backends are in PATH and skips isolated installation.
Backend overrides are for development only. Don’t use in production or CI/CD.

Debugging Builds

Inspecting Generated Recipes

Backends generate rattler-build recipes stored in your project:
# General recipe (all outputs)
.pixi/build/work/<package-name>--<hash>/debug/recipe.yaml
.pixi/build/work/<package-name>--<hash>/debug/variants.yaml

# Variant-specific recipe (single output)
.pixi/build/work/<package-name>--<hash>/debug/recipe/<variant_hash>/recipe.yaml
.pixi/build/work/<package-name>--<hash>/debug/recipe/<variant_hash>/variants.yaml

Rebuilding with rattler-build

Rerun a build directly:
cd .pixi/build/work/<package-name>--<hash>/debug/recipe/<variant_hash>/
rattler-build build
This helps:
  • Inspect exact build recipes
  • Debug build failures
  • Understand backend behavior
  • Compare variant configurations
The <variant_hash> ensures each unique combination of build variants gets its own recipe directory.

Examining JSON-RPC Communication

Find debug files in the recipe directory:
.pixi/build/work/<package-name>--<hash>/debug/
├── project_model.json      # Your project model
├── build_params.json       # Build request
└── build_response.json     # Build response
These files show the exact communication between Pixi and the backend.

Backend Development

Backends implement a protocol for:
  1. Manifest protocol - Understanding project configuration
  2. Build protocol - Executing builds
  3. Recipe generation - Creating rattler-build recipes

Creating a Custom Backend

Backends must:
  • Accept JSON-RPC requests on stdin
  • Return JSON-RPC responses on stdout
  • Implement required protocol methods
  • Generate valid rattler-build recipes
See pixi-build-backends for examples.

Backend Selection Guide

1
Identify Your Project Type
2
Python project with pyproject.toml? → Use pixi-build-python
3
C/C++ project with CMakeLists.txt? → Use pixi-build-cmake
4
Rust project with Cargo.toml? → Use pixi-build-rust
5
ROS package with package.xml? → Use pixi-build-ros
6
Need full control? → Use pixi-build-rattler-build
7
Check Language-Specific Requirements
8
Some backends need additional configuration:
9
# Python: specify PEP 517 backend
[package.host-dependencies]
hatchling = "*"

# C++: usually none (cmake backend provides tools)
[package.host-dependencies]
# Empty or specific libraries

# Rust: may need system dependencies
[package.host-dependencies]
openssl = "*"
10
Consider Build Complexity
11
Simple projects - Use language-specific backends Complex builds - Consider pixi-build-rattler-build for full control Multi-language - Use multiple packages with different backends

Advanced Topics

Key Concepts

Compilers

How pixi-build integrates with conda-forge’s compiler infrastructure for cross-platform builds.

Multiple Backends in a Workspace

Different packages can use different backends:
workspace/
├── pixi.toml              # Python package
│   [package.build]
│   backend = { name = "pixi-build-python" }
└── packages/
    └── cpp_math/          # C++ package
        [package.build]
        backend = { name = "pixi-build-cmake" }

Backend Versioning

Pin backends for reproducible builds:
# Exact version
[package.build]
backend = { name = "pixi-build-python", version = "==0.4.5" }

# Version range
[package.build]
backend = { name = "pixi-build-python", version = ">=0.4,<0.5" }

# Latest version
[package.build]
backend = { name = "pixi-build-python", version = "*" }

Real-World Examples

Mixed Python and C++

pixi.toml
[workspace]
channels = ["https://prefix.dev/conda-forge"]
platforms = ["linux-64", "osx-arm64", "win-64"]
preview = ["pixi-build"]

[dependencies]
my_package = { path = "." }

[package]
name = "my_package"
version = "0.1.0"

# Python backend for the main package
[package.build]
backend = { name = "pixi-build-python", version = "0.4.*" }

[package.host-dependencies]
hatchling = "*"

# C++ dependency uses cmake backend
[package.run-dependencies]
cpp_core = { path = "packages/cpp_core" }
packages/cpp_core/pixi.toml
[package]
name = "cpp_core"
version = "0.1.0"

# CMake backend for C++ package
[package.build]
backend = { name = "pixi-build-cmake", version = "0.3.*" }

[package.host-dependencies]
nanobind = "*"
python = ">=3.11"

Best Practices

Use specific version ranges for reproducible builds:
# Good
backend = { name = "pixi-build-python", version = "0.4.*" }

# Risky
backend = { name = "pixi-build-python", version = "*" }
Don’t force a backend:
# Good - Python project uses Python backend
[package.build]
backend = { name = "pixi-build-python" }

# Bad - Using rattler-build when language backend exists
[package.build]
backend = { name = "pixi-build-rattler-build" }
Let backends handle defaults:
# Good - minimal config
[package.build]
backend = { name = "pixi-build-cmake", version = "0.3.*" }

# Unnecessary - cmake backend provides these
[package.build-dependencies]
cmake = "*"
ninja = "*"
Comment why you chose a backend:
# Using rattler-build for complex multi-step build
[package.build]
backend = { name = "pixi-build-rattler-build", version = "0.3.*" }

Next Steps

pixi-build-cmake

Complete CMake backend documentation

pixi-build-python

Complete Python backend documentation

Dependency Types

How backends use different dependencies

Build Variants

Building multiple configurations

Troubleshooting

Ensure backend channel is included:
[package.build.backend]
channels = [
  "https://prefix.dev/pixi-build-backends",
  "https://prefix.dev/conda-forge",
]
name = "pixi-build-python"
Check generated recipe:
cat .pixi/build/work/<package>--<hash>/debug/recipe/<variant>/recipe.yaml
Try rebuilding with rattler-build directly for better error messages.
Explicitly specify compatible versions:
[package.build]
backend = { name = "pixi-build-cmake", version = ">=0.3,<0.4" }
Some backends may not support all features. Consider:
  • Switching to pixi-build-rattler-build for full control
  • Contributing to the backend repository
  • Opening an issue for missing features