Deploy to Your Robot

This guide walks through every step from "my project works on my PC" to "it's running on my robot." No prior SSH or networking experience required.

Problem Statement

Your HORUS project runs on your development machine and you need to transfer it to a physical robot (Raspberry Pi, Jetson, or other Linux board) and run it there.

When To Use

  • First-time deployment to a new robot
  • Setting up SSH key authentication for passwordless deploys
  • Configuring named deploy targets for repeated deployments
  • Deploying to a fleet of multiple robots at once

Prerequisites

  • A working HORUS project on your development PC
  • A Linux-based robot (Raspberry Pi, Jetson, or any ARM/x86 board)
  • Both machines on the same network (WiFi or Ethernet)

How It Works

horus deploy is a three-step pipeline that runs on your development PC:

Loading diagram...
horus deploy: build on your PC, sync via SSH, run on the robot

Your code is compiled on your PC and the finished binary is copied to the robot. The robot just runs it.

What Each Machine Needs

Your Development PC

RequirementWhyHow to install
HORUS CLIBuilds and deploys your projectInstallation guide
Rust toolchainCross-compiles for robot architectureInstalled with HORUS
rsyncSyncs files to robot efficientlysudo apt install rsync (usually pre-installed)
SSH clientConnects to robotsudo apt install openssh-client (usually pre-installed)

Supported dev PC operating systems: Linux, macOS, WSL 2. Native Windows without WSL is not supported (no rsync/SSH).

Your Robot

RequirementWhy
LinuxAny distribution (Raspberry Pi OS, Ubuntu, Debian, etc.)
SSH server enabledSo your PC can connect and copy files
Network connectionWiFi or Ethernet, same network as your PC

The robot does NOT need: HORUS, Rust, cargo, or any build tools. The compiled binary is self-contained.

Exception for Python projects: The robot also needs python3 and the HORUS Python package:

# Run this ON the robot (one-time setup)
pip install horus-robotics

Step-by-Step

Step 1: Prepare Your Robot

If you have a new Raspberry Pi or Jetson that isn't set up yet:

Raspberry Pi:

  1. Download Raspberry Pi Imager on your PC
  2. Flash an SD card with Raspberry Pi OS (or Ubuntu Server)
  3. In the imager settings (gear icon), configure:
    • Username and password (e.g., pi / yourpassword)
    • WiFi network name and password
    • Enable SSH (check the box)
  4. Insert the SD card into the Pi and power it on
  5. Wait 1-2 minutes for it to boot and connect to WiFi

Jetson Nano/Xavier/Orin:

  1. Flash the SD card or eMMC with NVIDIA JetPack (Ubuntu-based)
  2. Complete the first-boot setup (username, password, WiFi)
  3. SSH is enabled by default on JetPack

Any other Linux board:

  • Ensure SSH is enabled: sudo systemctl enable ssh && sudo systemctl start ssh
  • Ensure it's connected to the same network as your PC

Step 2: Find Your Robot's IP Address

Your PC and robot must be on the same network (same WiFi or same Ethernet switch). You need the robot's IP address to connect.

Option A: mDNS (easiest, try this first)

Most Linux boards advertise their hostname on the local network:

# On your PC:
ping raspberrypi.local    # Raspberry Pi default hostname
ping jetson.local          # Jetson default hostname

If it responds, you can use raspberrypi.local instead of an IP address:

horus deploy pi@raspberrypi.local --run

Option B: Check your router

  1. Open your router's admin page in a browser (usually 192.168.1.1 or 192.168.0.1)
  2. Look for "Connected Devices" or "DHCP Clients"
  3. Find your robot's hostname (e.g., "raspberrypi") and note its IP

Option C: Scan your network

# On your PC — scan for all devices:
nmap -sn 192.168.1.0/24

# Or if your network is 192.168.0.x:
nmap -sn 192.168.0.0/24

Look for your robot's hostname or MAC address in the results.

Option D: Connect a monitor to the robot

Plug a monitor and keyboard into the robot and run:

hostname -I
# Output: 192.168.1.50

Step 3: Test SSH Connection

Before deploying, verify you can connect to the robot:

ssh pi@192.168.1.50
# Enter your password when prompted

Replace pi with your robot's username and 192.168.1.50 with the IP you found. If you see the robot's terminal prompt, it works. Type exit to disconnect.

If "Connection refused": SSH is not enabled on the robot. Connect a monitor and keyboard to the robot and run:

sudo systemctl enable ssh
sudo systemctl start ssh

Without SSH keys, horus deploy will prompt for your password during every deployment. Setting up keys makes it passwordless:

# On your PC — generate a key (press Enter for all prompts):
ssh-keygen -t ed25519

# Copy it to the robot:
ssh-copy-id pi@192.168.1.50
# Enter your password one last time

# Verify — this should connect WITHOUT asking for a password:
ssh pi@192.168.1.50

Step 5: Deploy

In your project directory on your PC:

horus deploy pi@192.168.1.50 --run

You'll see:

HORUS Deploy

  Target:       pi@192.168.1.50
  Remote dir:   ~/horus_deploy
  Architecture: ARM64 (aarch64)
  Build mode:   release
  Run after:    true

Step 1: Building project...
  Checking target aarch64-unknown-linux-gnu... OK
  Building for ARM64 (aarch64)...
  Build complete

Step 2: Syncing files to target...
  Will sync '/home/you/my-robot' to pi@192.168.1.50:~/horus_deploy/ (with --delete)
  Files on remote not present locally will be DELETED
  Continue? [y/N] y
  Syncing files...
  Files synced

Step 3: Running on target...
  Running: ./target/aarch64-unknown-linux-gnu/release/my_robot
  Press Ctrl+C to stop

  [SCHEDULER] Started with 3 nodes at 100 Hz
  [motor_controller] Initialized
  ...

The binary is self-contained. The robot doesn't need Rust or cargo installed.

Press Ctrl+C to stop the robot.

Step 6: Save Your Target

Typing the full pi@192.168.1.50 every time gets tedious. Create a deploy.yaml file in your project root:

targets:
  robot:
    host: pi@192.168.1.50
    arch: aarch64

Now deploy with just a name:

horus deploy robot --run

For multiple robots, add more targets:

targets:
  robot:
    host: pi@192.168.1.50
    arch: aarch64
  jetson:
    host: nvidia@jetson.local
    arch: aarch64
    port: 2222
    identity: ~/.ssh/jetson_key
  arm-pc:
    host: ubuntu@10.0.0.20
    arch: x86_64
    dir: ~/my_app

Deploy Options

OptionWhat it doesExample
--runExecute the binary after deployinghorus deploy robot --run
--dry-runShow what would happen without doing ithorus deploy robot --dry-run
--archOverride target architecturehorus deploy robot --arch armv7
--debugBuild in debug mode (faster build, slower binary)horus deploy robot --debug
-d, --dirCustom remote directory (default: ~/horus_deploy)horus deploy robot -d /opt/robot
-p, --portCustom SSH port (default: 22)horus deploy robot -p 2222
-i, --identitySSH private key filehorus deploy robot -i ~/.ssh/robot_key
--allDeploy to every target in deploy.yamlhorus deploy --all --run
--listShow configured targetshorus deploy --list

Fleet Deployment

Deploy to all robots with one command:

horus deploy --all --run

This builds once and syncs to each robot. If one robot is unreachable, the others still get deployed:

HORUS Fleet Deploy (3 targets)
  1. robot    -> pi@192.168.1.50
  2. jetson   -> nvidia@jetson.local
  3. arm-pc   -> ubuntu@10.0.0.20

Step 1: Building for ARM64 (shared across 3 targets)...
  Build complete

Step 2: Syncing to 3 targets...
  This will sync to 3 remote hosts (with --delete)
  Continue? [y/N] y

--- [1/3] robot
  Files synced
  [1/3] robot done
--- [2/3] jetson
  [2/3] jetson done
--- [3/3] arm-pc
  [x] arm-pc failed: SSH connection refused

=== Fleet Deploy Summary
  2/3 targets deployed successfully

What Gets Copied to the Robot

The project directory is synced to ~/horus_deploy/ on the robot, excluding:

  • target/ (build artifacts — only the final binary is kept)
  • .git/ (version control history)
  • __pycache__/ and *.pyc (Python cache)
  • node_modules/ (JavaScript dependencies)

The rsync --delete flag keeps the remote directory in sync — files you delete locally are also deleted on the robot.

Architecture Auto-Detection

If you don't specify --arch, horus guesses from the hostname:

Hostname containsDetected architecture
jetson, nano, xavier, orinaarch64
pi4, pi5, raspberryaarch64
pi3, pi2armv7
Anything elseaarch64 (default)

Override with --arch if the auto-detection is wrong:

horus deploy myrobot@10.0.0.5 --arch x86_64

Rust vs Python Differences

AspectRust projectPython project
Build stepCross-compiles for target architectureSkipped (no compilation needed)
Robot requirementsNothing beyond Linux + SSHPython 3 + pip install horus-robotics
What runs on robotSingle binary (./target/.../my_robot)python3 src/main.py
Binary sizeSelf-contained (~5-50 MB)Source files only
First deploy speedSlower (compilation)Faster (just file sync)

Auto-Start on Boot (systemd)

To run your HORUS project automatically when the robot boots, create a systemd service:

# On the robot, create a service file:
sudo tee /etc/systemd/system/horus-robot.service << 'EOF'
[Unit]
Description=HORUS Robot Application
After=network.target

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/horus_deploy
ExecStart=/home/pi/horus_deploy/target/aarch64-unknown-linux-gnu/release/my_robot
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable horus-robot
sudo systemctl start horus-robot
# Check status
sudo systemctl status horus-robot

# View logs
journalctl -u horus-robot -f

Troubleshooting

"Connection refused" when deploying

SSH is not enabled or not running on the robot:

# Connect a monitor/keyboard to the robot and run:
sudo systemctl enable ssh
sudo systemctl start ssh

"Permission denied (publickey)"

The robot is rejecting your SSH key or password:

# Try with explicit password authentication:
ssh -o PreferredAuthentications=password pi@192.168.1.50

# If that works, re-copy your key:
ssh-copy-id pi@192.168.1.50

"rsync: command not found"

Install rsync on your development PC:

# Ubuntu/Debian:
sudo apt install rsync

# macOS:
brew install rsync

Binary crashes or "Exec format error" on robot

Wrong architecture. The binary was compiled for a different CPU than the robot has:

# Check what the robot actually runs:
ssh pi@192.168.1.50 "uname -m"
# aarch64 = use --arch aarch64
# armv7l  = use --arch armv7
# x86_64  = use --arch x86_64

# Re-deploy with the correct architecture:
horus deploy pi@192.168.1.50 --arch armv7 --run

"cargo build failed" during cross-compilation

The cross-compilation target may not be installed. Horus installs it automatically, but if it fails:

# Install manually on your PC:
rustup target add aarch64-unknown-linux-gnu

# For ARM 32-bit:
rustup target add armv7-unknown-linux-gnueabihf

You may also need the cross-compilation linker:

# Ubuntu/Debian:
sudo apt install gcc-aarch64-linux-gnu

# For ARM 32-bit:
sudo apt install gcc-arm-linux-gnueabihf

"ping raspberrypi.local" doesn't work

mDNS may not be available. Try:

# Install mDNS support on your PC:
sudo apt install avahi-utils

# Install on the robot (connect monitor/keyboard):
sudo apt install avahi-daemon

Or skip mDNS and find the IP through your router or nmap instead.

Deploy works but program doesn't find hardware (GPIO, serial, I2C)

The robot user may not have permission to access hardware devices:

# On the robot:
sudo usermod -aG dialout,gpio,i2c,spi pi
# Log out and back in for changes to take effect

See Also