A template repository for robotics engineers building ROS 2 applications on the Elodin Aleph flight computer using Nix and NixOS.
aleph-ros.mov
This project demonstrates how to integrate ROS 2 Humble with Aleph's Nix-based development workflow, providing a solid foundation for building autonomous systems, drones, and robotic platforms.
- ROS 2 Humble pre-configured and running on Aleph
- Nix-based reproducible builds for reliable deployments
- Four packaging patterns from simple to complex
- systemd integration for production-ready services
- Cross-compilation support via remote builders
flowchart TB
subgraph flake [flake.nix]
inputs[Aleph + nixpkgs + nix-ros-overlay]
overlay[Custom Overlay]
module[NixOS Modules]
end
subgraph pkgs [nix/pkgs/]
nixpkgEx[example-nixpkgs.nix]
sourceEx[example-from-source.nix]
helloPkg[hello-service.nix]
rosPkg[ros-hello.nix]
end
subgraph mods [nix/modules/]
helloMod[hello-service.nix]
rosMod[ros-hello.nix]
end
subgraph src [src/]
mainPy[hello-service/main.py]
end
subgraph ros [ROS 2 Humble]
roscore[ros-core]
demos[demo-nodes-py]
topics["chatter topic"]
end
overlay --> nixpkgEx
overlay --> sourceEx
overlay --> helloPkg
overlay --> rosPkg
module --> helloMod
module --> rosMod
helloPkg --> src
rosPkg --> ros
rosMod --> topics
Before you begin, ensure you have:
- Determinate Systems Nix installed on your development machine
- An Aleph flight computer with the base NixOS image flashed
- Network connectivity to your Aleph (WiFi or USB ethernet)
- SSH access configured (password or key-based)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- installgit clone https://github.com/elodin-sys/aleph-template-project.git
cd aleph-template-projectTest that everything compiles correctly:
nix build --accept-flake-config .#packages.aarch64-linux.toplevel --show-traceNote: The first build downloads ROS 2 packages from the nix-ros-overlay Cachix cache, which significantly speeds up builds.
Once connected to your Aleph over the network:
./deploy.sh -h <aleph-hostname-or-ip> -u alephFor example:
./deploy.sh -h aleph-24a5.local -u aleph
./deploy.sh -h 192.168.4.181 -u alephSSH into your Aleph and verify ROS 2:
ssh -i ./ssh/aleph-key aleph@<aleph-ip>
# Check the ROS 2 talker service is running
systemctl status ros-hello
# Watch the ROS 2 output
journalctl -u ros-hello -f
# List ROS 2 topics
ros2 topic list
# Echo messages from the talker
ros2 topic echo /chatterYou should see output like:
[INFO] [talker]: Publishing: "Hello World: 42"
[INFO] [talker]: Publishing: "Hello World: 43"
...
If your Aleph is fresh out of the box or you've lost network connectivity, you'll need to configure it via the serial console.
- Connect a USB cable to the Aleph's FTDI debug port
- Find the serial device:
ls /dev/tty.usbserial-* # macOS ls /dev/ttyUSB* # Linux
- Connect with screen:
screen /dev/tty.usbserial-XXXXX 115200 # or on Linux: screen /dev/ttyUSB0 115200 - Press Enter to get a login prompt
- Login as
root, default password isroot
Once connected via serial, use iwctl to configure WiFi:
# Start the interactive WiFi tool
iwctl
# Inside iwctl:
station wlan0 scan
station wlan0 get-networks
station wlan0 connect "YourNetworkName"
# Enter password when prompted
exit
# Verify connection
ip addr show wlan0
ping -c 3 google.com# On the Aleph (via serial or existing SSH):
ip addr show wlan0
# Or use mDNS from your development machine:
ping aleph-XXXX.local # where XXXX is the last 4 chars of the serial numberaleph-template-project/
├── flake.nix # Main Nix configuration (overlay, modules, dev shell)
├── flake.lock # Locked dependency versions
├── deploy.sh # Deployment script
├── README.md # This file
├── nix/
│ ├── modules/
│ │ ├── hello-service.nix # Python service module (Pattern 3)
│ │ ├── ros-hello.nix # ROS 2 service module (Pattern 4)
│ │ └── python-env.nix # Unified Python environment (pyzed, numpy, opencv, etc.)
│ └── pkgs/
│ ├── example-nixpkgs.nix # Using nixpkgs (Pattern 1)
│ ├── example-from-source.nix # Building from source (Pattern 2)
│ ├── hello-service.nix # Local Python package (Pattern 3)
│ ├── ros-hello.nix # ROS 2 package (Pattern 4)
│ ├── zed-sdk.nix # Stereolabs ZED SDK (x86_64 + Jetson)
│ └── pyzed.nix # ZED Python API (built from source)
├── src/
│ └── hello-service/
│ └── main.py # Python application source
└── ssh/
├── aleph-key # SSH private key
└── aleph-key.pub # SSH public key
This template demonstrates four common patterns for adding software to your Aleph:
File: nix/pkgs/example-nixpkgs.nix
The simplest pattern—use packages that already exist in nixpkgs. This example wraps btop (a modern resource monitor) with a custom launcher script called aleph-monitor.
# In your overlay:
example-nixpkgs = final.callPackage ./nix/pkgs/example-nixpkgs.nix {};
# In environment.systemPackages:
example-nixpkgs # Available as 'aleph-monitor' commandWhen to use: For well-supported packages in nixpkgs that you want to include or customize.
File: nix/pkgs/example-from-source.nix
Fetch source code from GitHub and build it. This example builds lazygit from the official repository.
# Key elements:
src = fetchFromGitHub {
owner = "jesseduffield";
repo = "lazygit";
rev = "v${version}";
hash = "sha256-..."; # Use nix-prefetch-github to get this
};When to use: For packages not in nixpkgs, specific versions, forks, or custom patches.
Files:
src/hello-service/main.py— Python source codenix/pkgs/hello-service.nix— Nix package definitionnix/modules/hello-service.nix— NixOS module with systemd service
This pattern packages your own Python application and deploys it as a managed systemd service with configurable options.
# In flake.nix:
services.hello-service = {
enable = true;
message = "Hello from Aleph Template Project!";
interval = 30;
};When to use: For your own applications that need to run as background services.
Files:
nix/pkgs/ros-hello.nix— ROS 2 environment packagenix/modules/ros-hello.nix— NixOS module with systemd service
This pattern uses nix-ros-overlay to build ROS 2 environments and run ROS nodes as systemd services.
# In nix/pkgs/ros-hello.nix:
let
rosEnv = rosPackages.humble.buildEnv {
paths = with rosPackages.humble; [
ros-core
demo-nodes-py
demo-nodes-cpp
];
};
in
stdenv.mkDerivation {
# ... wrapper around ros2 run
}
# In flake.nix:
services.ros-hello = {
enable = true;
};When to use: For ROS 2 robotics applications, sensor processing, motion control, and autonomous systems.
Create a new package in nix/pkgs/my-ros-node.nix:
{ lib, rosPackages, stdenv, makeWrapper }:
let
rosEnv = rosPackages.humble.buildEnv {
paths = with rosPackages.humble; [
ros-core
rclpy # Python ROS 2 client
std-msgs # Standard message types
sensor-msgs # Sensor message types
geometry-msgs # Geometry message types
# Add more ROS packages as needed
];
};
in
stdenv.mkDerivation {
pname = "my-ros-node";
version = "1.0.0";
dontUnpack = true;
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
mkdir -p $out/bin
makeWrapper ${rosEnv}/bin/ros2 $out/bin/my-ros-node \
--add-flags "run my_package my_node"
'';
}Create nix/modules/my-ros-node.nix:
{ config, lib, pkgs, ... }:
with lib;
let cfg = config.services.my-ros-node;
in {
options.services.my-ros-node = {
enable = mkEnableOption "My ROS 2 node";
};
config = mkIf cfg.enable {
systemd.services.my-ros-node = {
description = "My ROS 2 Node";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment = {
HOME = "/var/lib/my-ros-node";
ROS_HOME = "/var/lib/my-ros-node/.ros";
ROS_LOG_DIR = "/var/lib/my-ros-node/.ros/log";
};
serviceConfig = {
ExecStart = "${pkgs.my-ros-node}/bin/my-ros-node";
StateDirectory = "my-ros-node";
Restart = "always";
};
};
};
}Add to the overlay and enable the module:
# In overlays.default:
my-ros-node = final.callPackage ./nix/pkgs/my-ros-node.nix {};
# In nixosModules.default imports:
./nix/modules/my-ros-node.nix
# Enable the service:
services.my-ros-node.enable = true;The nix-ros-overlay provides 1500+ ROS 2 packages for Humble. Common packages include:
| Category | Packages |
|---|---|
| Core | ros-core, rclpy, rclcpp |
| Messages | std-msgs, sensor-msgs, geometry-msgs, nav-msgs |
| Perception | image-transport, cv-bridge, pcl-ros |
| Navigation | nav2-bringup, slam-toolbox, robot-localization |
| Control | ros2-control, ros2-controllers |
| Visualization | rviz2, rqt |
| Simulation | gazebo-ros-pkgs |
To find available packages:
# In a nix repl:
nix repl
:lf .
rosPackages.humble.<TAB>git clone https://github.com/elodin-sys/aleph-template-project.git my-robot-project
cd my-robot-project
rm -rf .git
git initrm ssh/aleph-key ssh/aleph-key.pub
ssh-keygen -t ed25519 -f ssh/aleph-key -C "my-robot-project"Update flake.nix with your new public key:
users.users.aleph = {
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAA... your-new-key"
];
};Edit nix/pkgs/ros-hello.nix to include the ROS 2 packages your robot needs:
rosEnv = rosPackages.humble.buildEnv {
paths = with rosPackages.humble; [
ros-core
sensor-msgs
geometry-msgs
nav-msgs
tf2-ros
robot-state-publisher
# Your custom packages...
];
};Once you're comfortable, remove the example patterns:
- Delete
nix/pkgs/example-*.nix - Delete
nix/modules/hello-service.nixandsrc/hello-service/ - Keep or modify
ros-helloas a starting point
This project includes a Nix dev shell with the Stereolabs ZED SDK 5.1 and Python bindings (pyzed, numpy, OpenCV, matplotlib), ready for use with a ZED camera connected to your x86_64 Ubuntu laptop via USB.
Connect your ZED camera via USB, then:
nix developOn the first run the shell will ask for your sudo password to install USB access rules for the camera (one-time). After that, every nix develop is instant.
Open a live camera feed with OpenCV (press q to quit):
python3 src/zed-viewer.pyVerify the SDK can talk to the camera without needing a GUI:
python3 src/zed-test.pyWith a ZED camera plugged into the Aleph, you can view the live feed on your laptop using SSH X11 forwarding. Copy the remote viewer script to the Aleph and run it:
scp -i ./ssh/aleph-key src/zed-viewer-remote.py aleph@<aleph-ip>:
ssh -X -i ./ssh/aleph-key aleph@<aleph-ip> "python3 zed-viewer-remote.py"A window will appear on your laptop showing the Aleph's camera feed. Press q to quit.
Note: for best results, use a powered USB-C 3.0 hub connected to the Aleph SuperSpeed port (middle side), to avoid any negotiation issues.
Uses your local machine or configured remote builders:
./deploy.sh -h <host> -u aleph -k ./ssh/aleph-keyIf you don't have an aarch64 builder, the script will automatically build on the Aleph itself:
./deploy.sh -h <host> -u aleph -k ./ssh/aleph-key
# Script will detect missing builder and use AlephForce local/configured builder usage:
./deploy.sh -h <host> -u aleph -k ./ssh/aleph-key --no-aleph-builder# Check service status
systemctl status ros-hello
# View detailed logs
journalctl -u ros-hello -f
# Common issues:
# 1. Log directory permissions - ensure StateDirectory is set
# 2. Missing ROS packages - check rosEnv paths
# 3. Environment variables - verify HOME/ROS_HOME are set# Ensure ROS 2 environment is sourced
ros2 topic list
# Check if the node is running
ros2 node list
# Verify network settings (for multi-machine setups)
export ROS_DOMAIN_ID=0- Check physical connection: Ensure Aleph is powered and connected
- Try mDNS:
ping aleph-XXXX.local - Fall back to serial: Connect via FTDI and check network config
- Check WiFi: Run
iwctlto verify/reconfigure WiFi
-
Hash mismatch: Update the hash in your package definition
-
Missing ROS dependencies: Add packages to your
rosEnv.paths -
Evaluation errors: Run with
--show-tracefor detailed errorsnix build --accept-flake-config .#packages.aarch64-linux.toplevel --show-trace
-
Verify the key file exists and has correct permissions:
ls -la ssh/aleph-key chmod 600 ssh/aleph-key
-
Ensure the public key is in
flake.nix -
Use the
alephuser (notroot) for deployment:./deploy.sh -h <host> -u aleph
# Build the system
nix build --accept-flake-config .#packages.aarch64-linux.toplevel
# Note for the first deploy you need to deploy as root to install the user and key
./deploy.sh -h <host> -u root # it will prompt you for password: "root"
# Deploy to Aleph
./deploy.sh -h <host> -u aleph # no prompt, seamless deploy
# SSH to Aleph
ssh -i ./ssh/aleph-key aleph@<host># ROS 2 commands
ros2 topic list
ros2 topic echo /chatter
ros2 node list
ros2 node info /talker
# Service management
systemctl status ros-hello
journalctl -u ros-hello -f
systemctl restart ros-hello
# System info
neofetch
btop
# Network info
ip addr
iwctl station wlan0 show
# Check NixOS generation
nixos-rebuild list-generations- nix-ros-overlay - ROS packages for Nix
- ROS 2 Humble Documentation
- ROS 2 Tutorials
This template is provided under the Apache-2.0 license. See LICENSE for details.