Jafar Uruç

Using Pixi as a Development Environment for ROS 2

2025-08-23

When starting a new ROS 2 project with multiple team members, establishing a reproducible development environment is crucial for success. Without it, you'll inevitably face the classic "works on my machine" problem, where code that runs perfectly for one developer fails mysteriously for another.

What is RoboStack?

RoboStack brings the Robot Operating System (ROS) to any platform - Linux, macOS, and Windows - using the Conda package manager. Unlike traditional ROS installations that are Linux-specific and require system-level packages, RoboStack packages ROS distributions (like Humble and Jazzy) as Conda packages that can be installed without root access.

This approach enables easy integration of ROS with machine learning libraries like PyTorch and Flax in the same environment, making it perfect for robotics research that combines traditional robotics with modern AI workflows.

Enter Pixi: A Unified Development Solution

One approach I used in the past was to create a Docker image that encapsulated all the dependencies and configurations needed for the project. This worked well for ensuring consistency across machines, but it also introduced significant complexity and frustration (see Appendix: Docker Challenges for details).

In this post, I'll introduce pixi as a modern alternative for creating reproducible ROS 2 development environments.

What Pixi Brings to the Table

For comprehensive documentation, visit pixi.sh.

Getting Started: Initial Setup

Begin by initializing Pixi in your project directory:

pixi init

Next, configure the appropriate channels. The channel selection depends on your target ROS 2 distribution. You can either manually edit your pixi.toml file or use the CLI:

# Add RoboStack channel for your desired ROS 2 distribution
pixi workspace channel add https://prefix.dev/robostack-jazzy

This will update your pixi.toml file with:

channels = ["conda-forge", "https://prefix.dev/robostack-jazzy"]

Replace robostack-jazzy with your desired ROS 2 distribution (e.g., robostack-humble for Humble).

ROS 2 Base Dependencies

Add ROS 2 dependencies using the CLI:

# Add one of the following based on your project needs:
pixi add ros-jazzy-ros-core        # Minimal core functionality
pixi add ros-jazzy-ros-base        # Standard base installation (recommended)
pixi add ros-jazzy-desktop         # Desktop environment with GUI tools
pixi add ros-jazzy-desktop-full    # Complete desktop installation

Pro Tip: Use the prefix.dev package explorer to browse available packages and their dependencies. For CLI users, run: pixi search --channel https://prefix.dev/robostack-jazzy ros-jazzy-desktop

For common development tools, add them with:

pixi add colcon-common-extensions colcon-mixin cmake ninja mold sccache

Dependency Management Strategy

I'm not aware of a tool that automatically extracts dependencies from package.xml files and updates the pixi.toml file. Hence, you will need to either manually add the required packages to your pixi.toml through the pixi add <package-name> command or by creating a script that parses package.xml files and updates the pixi.toml file accordingly with pixi add $(my-cool-vibe-coded-script).

Task Automation

Define your tasks using the CLI:

pixi task add build "colcon build"
pixi task add test "colcon test" --depends-on build

Execute tasks with simple commands:

pixi run test  # Automatically runs build first, then test

Advanced Task Features Worth Exploring: - Task arguments for parameterized execution - Task caching for improved performance

Multi-Environment Support

One of Pixi's most powerful features is seamless multi-environments support. You can set this up using CLI commands:

# Setup Humble environment
pixi workspace channel add https://prefix.dev/robostack-humble --feature humble
pixi workspace environment add humble --feature humble
pixi add ros-humble-ros-base --feature humble

# Setup Jazzy environment
pixi workspace channel add https://prefix.dev/robostack-jazzy --feature jazzy
pixi workspace environment add jazzy --feature jazzy
pixi add ros-jazzy-ros-base --feature jazzy

This creates the following configuration in your pixi.toml:

[feature.jazzy.dependencies]
ros-jazzy-ros-base = "*"

[feature.humble.dependencies]
ros-humble-ros-base = "*"

[environments]
humble = { features = ["humble"] }
jazzy = { features = ["jazzy"] }

Switch between environments effortlessly:

pixi shell -e jazzy   # Enter Jazzy environment
pixi shell -e humble  # Switch to Humble environment

You can also create specialized environments for different purposes:

# Setup linting environment
pixi add pre-commit --feature lint
pixi workspace environment add lint --feature lint --no-default-feature
pixi task add lint "pre-commit run --all-files" --feature lint
pixi run lint

This eliminates the need for multiple Docker containers.

PyPi dependencies

Pixi uses uv for managing PyPI dependencies alongside Conda packages. To add PyPI packages, use:

pixi add --pypi numpy scipy matplotlib

It also supports extras. For example, to add Flask with async support:

pixi add --pypi flask[async]

This will update your pixi.toml file with:

[pypi-dependencies]
flask = { version = ">=3.1.2, <4", extras = ["async"] }

If you want to add a source python package, you can do so by specifying the path:

[pypi-dependencies]
my_cool_library = { path = "./my_cool_library", editable = true, extras = ["dev"] }

Cross-Platform Support

Most of the RoboStack packages are available in linux-64, linux-aarch64, osx-64, osx-arm64, and win-64. If you are lucky enough and all your other dependencies are available on these platforms, you can use Pixi to create a cross-platform ROS 2 development environment.

Streamlined CI/CD Workflows

This part is what I'm mostly excited about when using Pixi. Pixi ensures that:

While tools like industrial_ci remain valuable, Pixi's reproducibility makes debugging significantly more straightforward.

Example Implementation

For a practical demonstration, explore this complete ROS 2 project using Pixi: pixi_ros2_example.

The project showcases: - Dual support for Humble and Jazzy distributions - Clean, self-contained structure - Automated CI/CD integration

Project Structure:

project_root/
├── .github/workflows/ci.yaml    # CI/CD configuration
├── pixi.toml                    # Project configuration
├── pixi.lock                    # Dependency lock file
├── colcon_defaults.yaml         # Colcon settings
├── build/                       # Build artifacts
├── install/                     # Installation directory
├── pkg1/                        # ROS 2 package 1
├── pkg2/                        # ROS 2 package 2
└── ...

Bonus: Git Worktree Integration

I'm a big fan of git-worktree. With this approach, I can easily manage multiple branches of the same repository without needing to clone it multiple times or stash changes when switching branches. This is particularly useful for ROS 2 development, where you might want to work on multiple features or bug fixes simultaneously.

Simply running git worktree add ../worktree_dirname branch_name creates a new directory with the specified branch checked out, allowing you to work on it independently.

Current Limitations and Considerations

[activation.env]
# In v0.51.0 the expansion is not performed, so the value is literally "$CONDA_PREFIX/my_env_var"
MY_ENV_VAR="$CONDA_PREFIX/my_env_var"

Update: Experimental Build Backends for ROS Packages

Austin Gregg-Smith and Silvio Traversaro told me about an exciting new feature in Pixi, it now includes experimental build backends that can build ROS 2 packages directly from source code.

See ros_workspace for an example of how to use this feature.

Conclusion

As someone who has been working in robot learning where projects require a mix of Python and C++ codebases alongside complex dependency management spanning both ROS 2 and ML frameworks, Pixi has been a game-changer. The ability to seamlessly manage conda-forge packages for ROS 2 components alongside PyPI packages for machine learning dependencies in a single, reproducible environment has dramatically reduced development friction and allowed me to iterate much faster.

Appendix: Advanced Pixi Usage

Beyond ROS 2, I've successfully used Pixi to build complex projects like OMPL, MuJoCo, MuJoCo MPC, and llama.cpp from source - you can find these configurations in my pixi_workspaces repository.

Appendix: Docker Challenges in ROS 2 Development

Having worked with Docker for ROS 2 development, the typical workflow involves three files: a Dockerfile with several stages (base image with ROS 2 installed, dependency installation, build stage to compile ROS packages, and final dev stage with necessary tools), a docker-compose file defining services like the dev environment and CI/CD testing that mount the workspace and set environment variables, and finally a Makefile that orchestrates the build and run commands.

However, this approach has several significant drawbacks:

xkcd: Compiling

Typical xkcd developer waiting for Docker to build a ROS 2 workspace.

Heaviest objects in the universe

Heaviest objects in the universe: neutron stars, black holes, and Docker images. (Source: Bluesky post)


Acknowledgments

Thanks to Sam Pfeiffer and Sebastian Castro for reviewing this post and providing helpful feedback.

#ros2 #pixi