Robotics software is a dependency management problem. You’ve got ROS packages, C toolchains, cross-compilation targets, and embedded boards—all needing to stay in sync across development, CI, and production.
Nix is one solution. Here’s what it actually does and who it’s for.
What Nix Solves
The core problem: C has no package manager. ROS helps with message-passing and hardware abstraction, but the underlying dependency graph is still chaos. Different machines, different builds.
Nix’s approach: Every package is a function of its inputs. Change any input, the hash changes, it’s a different package. Builds happen in sandboxes. If it builds once, it builds the same way everywhere.
Clearpath Robotics presented results at ROSCon 2022: - Build resources: 144 cores → 12 cores - Daily update size: gigabytes → tens of megabytes - Pipeline time: 1.5 hours → 20 minutes
Those numbers come from true delta builds. You only rebuild and download what actually changed.
Who Should Consider This
Nix makes sense if: - You’re managing multiple ROS distributions or hardware targets - You need reproducible builds for certification or long-term support - Your team wastes time debugging “works on my machine” issues - You’re deploying to embedded targets where closure size matters
Nix probably doesn’t make sense if: - You’re a small team with simple dependencies - You don’t have time to learn a new build system - Your current Docker/apt workflow is working fine
The learning curve is real. The Nix language is unfamiliar. The conceptual model is different from apt or pip. Host-Nix interactions (CUDA, Qt, glibc) can be finicky.
The ROS Integration
nix-ros-overlay packages ROS 2 for Nix. Supports Jazzy and Rolling. Prebuilt binaries via Cachix.
nix develop github:lopsided98/nix-ros-overlay/master#example-ros2-desktop-jazzyThat gives you a complete ROS 2 environment. No manual setup.
Clearpath’s nix-ros-base takes a different approach optimized for CI—source snapshots, colcon builds, no bloom-release required.
Embedded Targets
Nix doesn’t run on your ESP32, but it can build for it. Projects like nixpkgs-esp-dev provide reproducible toolchains.
The value: your firmware build is a derivation with a hash. You know exactly what’s on the board. Useful for compliance, debugging production issues, or just not losing your mind.
Hardware Configuration as Code
Here’s an idea worth considering: Nix solves the software configuration problem. What about the hardware configuration problem?
The Tribal Knowledge Problem
Robotics has a chaos problem beyond software. Every robot system has layers of hardware-specific configuration:
- Pin multiplexing: Which GPIO does what? On an STM32 or i.MX processor, most pins can serve multiple functions. UART0_TX might also be GPIO21, SPI1_CS, or an I2C line depending on configuration.
- Sensor configurations: I2C addresses, sample rates, interrupt lines, power sequencing requirements.
- Board revisions: Rev 5.0 of your motor controller needs a different switch board than rev 4.7. The 3.3V regulator moved. The LED indicators changed pins.
- Peripheral mappings: Which CAN bus talks to which actuators? What’s the baud rate? The termination resistor situation?
This knowledge lives in wikis, spreadsheets, Slack threads, and the heads of engineers who’ve been around since v1. When something breaks in production, you’re reverse-engineering what hardware state the robot actually has.
Device Trees: The Existing Model
Embedded Linux already has a partial solution: the Device Tree. It’s a data structure describing hardware—processors, buses, peripherals, pin configurations—separate from kernel code. Instead of hardcoding “I2C bus 1 has a temperature sensor at address 0x40,” you declare it:
&i2c1 {
temp_sensor: si7020@40 {
compatible = "silabs,si7020";
reg = <0x40>;
};
};Device trees can be modified with overlays—fragments that extend or override the base configuration. Raspberry Pi uses this extensively for HATs. Industrial systems use it for daughterboards. The kernel loads the appropriate overlay at boot based on detected hardware.
The limitation: device trees describe hardware to the kernel, but they don’t version-control the relationship between hardware config, firmware, and software. They don’t give you a hash representing your complete system state.
The Nix Extension
Unlike consumer PCs where you run nixos-generate-config because you genuinely don’t know what’s inside, robots are known systems. You designed the board. You picked the sensors. You know the pinout. This is an advantage.
NixOS already has device tree support built in. From nixos/modules/hardware/device-tree.nix, you can specify overlays declaratively:
hardware.deviceTree = {
enable = true;
overlays = [
{ name = "motor-controller-v2";
dtboFile = ./overlays/motor-v2.dtbo; }
];
};The idea: extend this model further. Represent the complete hardware configuration—device trees, PlatformIO configs, peripheral mappings, board revision identifiers—in Nix alongside your software. Lock them into the same derivation that builds your firmware.
What This Looks Like in Practice
Consider a robot with three board revisions in the field. Today, you probably manage this with:
- Build flags (
-DBOARD_REV=5) - Runtime detection (reading GPIO pins or EEPROM to determine revision)
- Manual tracking of which firmware goes on which hardware
With hardware-config-as-code, each revision becomes a distinct derivation:
robotFirmware = {
board-v4 = buildFirmware {
deviceTree = ./dts/robot-v4.dts;
platformioEnv = "nucleo_v4";
sensors = { imu = "mpu6050"; lidar = "rplidar-a1"; };
};
board-v5 = buildFirmware {
deviceTree = ./dts/robot-v5.dts;
platformioEnv = "nucleo_v5";
sensors = { imu = "bmi088"; lidar = "rplidar-a2"; };
};
};The hash now represents your complete system state—software and hardware config together. Board v4 with MPU6050 is a fundamentally different derivation than board v5 with BMI088. The dependency graph makes this explicit.
Cross-Compilation Fits Naturally
Nix has mature cross-compilation support via pkgsCross. From an x86 workstation, you can target ARM, RISC-V, or any supported architecture:
nix build nixpkgs#pkgsCross.aarch64-multiplatform.helloThis extends to embedded targets. The nixpkgs-esp-dev project provides ESP32 toolchains as Nix derivations. You can build U-Boot for a custom board with a single expression:
pkgsCross.aarch64-multiplatform.buildUBoot {
defconfig = "my_custom_board_defconfig";
extraMeta.platforms = ["aarch64-linux"];
filesToInstall = ["u-boot-sunxi-with-spl.bin"];
}When your hardware configuration lives in the same Nix expression, cross-compilation inherits the context. The toolchain knows the target. The device tree gets compiled for the right architecture. The firmware includes the correct peripheral drivers.
Fleet Debugging Changes
Robot fleet management is hard. You’ve got devices deployed across facilities, running different firmware versions, with intermittent connectivity and limited diagnostic access. When “robot 47 is misbehaving,” the debugging process typically involves:
- SSH into the robot (if you can reach it)
- Check firmware version, configuration files, logs
- Try to reproduce locally with similar hardware
- Discover the production robot has a slightly different sensor, different calibration, different something
With Nix-based hardware configuration:
- Robot 47’s system state is hash
abc123def - You can reproduce that exact state locally by building the same derivation
- The hash captures not just software versions but device tree configuration, sensor parameters, and board revision
- Delta updates mean you’re only pushing what actually changed—tens of megabytes instead of gigabytes for full image reflashes
Certification and Compliance
IEC 61508 and ISO 13849 require documented traceability from requirements through design to deployment. Auditors want to verify that the configuration tested is the configuration shipped.
Nix’s content-addressed storage provides this naturally. The hash is a cryptographic proof of configuration identity. If the tested system has hash X, and production systems have hash X, they’re identical—not “probably the same” but mathematically identical.
For products with 10-25 year support lifecycles (medical devices, industrial robots, infrastructure), Nix’s pinning model means you can rebuild the exact firmware years later. The derivation records the specific compiler version, library versions, and configuration options. No drift.
The Gap: What’s Not Solved
This isn’t turnkey tooling today. You’d be building some of this yourself:
- Runtime hardware detection: Nix handles build-time configuration well. Runtime adaptation (detecting board revision via GPIO, loading appropriate overlays) requires additional infrastructure.
- Calibration data: Per-unit calibration (motor constants, sensor offsets) lives outside the firmware derivation. You need a separate system for this.
- Physical labeling: A hash doesn’t help if you can’t identify which physical robot has which hash. You still need asset tracking.
- OTA delivery: Nix builds the artifacts. Getting them to robots in the field is a separate problem (though the delta-update model helps).
Why Consider This Direction
If you’re already investing in Nix for software reproducibility, extending to hardware configuration is a natural next step:
- Unified model: One conceptual framework for both software and hardware-adjacent configuration.
- Explicit dependencies: Board revision v5 depends on sensor library v2.3 depends on firmware protocol v4. The graph makes this visible.
- Reproducibility at system level: Not just “same code” but “same device tree, same pin config, same peripheral setup.”
- Atomic rollback: If an OTA update breaks something, roll back to the previous derivation—including the hardware config that worked.
The Embedded Artistry folks have documented how most embedded projects don’t provide a method for software to determine hardware revision until it’s too late. GPIO-encoded version pins, EEPROM identifiers, ADC-based resistor dividers—all workarounds for a problem that declarative configuration could address upstream.
This is a direction, not a product. But if you’re thinking about build systems for robotics and you’re already learning Nix, it’s worth designing with this model in mind.
Questions to Ask
Before adopting Nix for robotics:
How much time do you spend on dependency issues? If the answer is “not much,” Nix might be overkill.
Do you need builds to be reproducible years from now? Medical devices, industrial equipment, anything with long support cycles—Nix handles this well.
How many hardware targets? Single target is manageable any way. Ten targets with different architectures is where Nix starts paying off.
Does your team have capacity to learn? Budget 2-4 weeks for engineers to get comfortable. It’s an investment.
What’s your CI infrastructure? Nix wants its own cache. You’ll need to set up Cachix or self-host.
Trade-offs
Gains: Reproducibility, smaller closures, true delta updates, unified tooling across ROS 1/2, declarative configuration.
Costs: Learning curve, occasional friction with non-Nix tooling, community smaller than Docker/apt ecosystems.
The Clearpath results are compelling, but they had engineers dedicated to the migration. Your mileage depends on your team’s situation.
Getting Started
If you want to evaluate:
- Install Nix on one machine (doesn’t replace your existing package manager)
- Try
nix developwith nix-ros-overlay - Build one package, see how it feels
- Decide if the model fits your workflow
The ROSCon 2022 talk is worth watching for a realistic picture of what adoption looks like.
Is Nix right for your robotics project? That depends on your dependency complexity, team capacity, and timeline. The tooling exists and works. The question is whether it’s worth the transition cost for your specific situation.