> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyberwave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prepare your Jetson Orin Nano

> Boot your Jetson Orin Nano from an NVMe SSD, configure headless operation, and set up network failover.

<Warning>
  **STUB DOCUMENT:** This page is intentionally minimal and will be expanded
  with deeper technical details in a future update.
</Warning>

This guide covers installing JetPack on your Jetson Orin Nano, using an NVMe SSD for reliable storage, and configuring the Jetson for headless operation with WiFi and optional 5G modem failover.

<Info>
  Cyberwave supports **JetPack 6.x and JetPack 7.x**, but we **strongly
  recommend JetPack 7.2 or later** (L4T r38+, Ubuntu 24.04): some features —
  including the GPU-accelerated ML worker image — are only available on
  JetPack 7.x. This guide targets the recommended JetPack 7.2+ path.
</Info>

<Warning>
  **Starting with JetPack 7.2, SD card images are no longer supported.** Do
  **not** flash the Jetson ISO to a microSD card. Write the ISO to a **USB
  flash drive** instead, then use the installer to write Jetson Linux onto your
  microSD card or NVMe SSD.
</Warning>

**Requirements:**

* Jetson Orin Nano Developer Kit
* **JetPack 7.2 or later recommended** (L4T r38+, Ubuntu 24.04); JetPack 6.x also works, but some features require JetPack 7.x
* **USB flash drive (16GB or larger)** for the JetPack 7.2+ installer ISO
* Target storage — a **PCIe NVMe SSD** (recommended for sustained workloads) and/or a microSD card (64GB UHS-1 or larger)
* A computer with [Balena Etcher](https://etcher.balena.io/) installed to write the ISO
* A DisplayPort monitor, USB keyboard, and mouse for the install

<Warning>
  SATA SSDs do not work on the Jetson Orin Nano — the M.2 slot only supports
  PCIe NVMe drives (PCIe 3.0 recommended — PCIe 4.0 may cause issues).
</Warning>

***

## Step 1: Verify board firmware

JetPack 7.2 requires JetPack 6.x-generation UEFI/QSPI firmware (version **36.x or newer**). Check your firmware version from the UEFI setup screen. If the Jetson shipped on older factory firmware, complete the JetPack 6.x firmware update path first — see the [official NVIDIA quick start guide](https://docs.nvidia.com/jetson/orin-nano-devkit/user-guide/latest/quick_start.html).

The installer will also prompt for a QSPI firmware update during Step 4; make sure to accept it.

***

## Step 2: Write the Jetson ISO to a USB flash drive

1. **Download the Jetson ISO.** Get the JetPack 7.2+ Jetson ISO (e.g. `r39.2`) from the [JetPack download page](https://developer.nvidia.com/embedded/jetpack).
2. **Write it to a USB flash drive.** Open [Balena Etcher](https://etcher.balena.io/), select the ISO, select your USB flash drive (16GB+), and click **Flash**.

<Warning>
  Simply copying the ISO file onto the USB drive will **not** make it bootable —
  you must write it with imaging software such as Balena Etcher. And per the
  note above, the ISO goes on the **USB drive**, never on the microSD card.
</Warning>

***

## Step 3: Install your target storage

Power off the Jetson and install the storage you want Jetson Linux written to:

* **NVMe SSD (recommended):** install it into the M.2 Key M slot on the underside of the carrier board and secure it with the screw.
* **microSD card:** insert it into the slot on the module underside.

An NVMe SSD is strongly recommended for any sustained workload — microSD cards degrade over time with frequent writes.

***

## Step 4: Boot the installer and install Jetson Linux

1. Connect a DisplayPort monitor, USB keyboard/mouse, and the installer **USB flash drive**, then power on.
2. Press **Esc** to enter UEFI, open the **Boot Manager**, and select the USB drive.
3. When prompted to update the **QSPI firmware**, press **Y** within 30 seconds to confirm. Allow any UEFI capsule updates to finish — the board may reboot, which is normal.
4. Select your **target storage** (NVMe SSD or microSD card) and confirm. The installer writes Jetson Linux and **handles partitioning and boot configuration automatically** — no manual `dd`, `extlinux`, or partition-resize steps are needed.
5. When it finishes, reboot from the target storage and complete initial setup (accept the EULA, choose language/keyboard/timezone, connect to the network, create your user account).

<Tip>
  After first boot, select **MAXN SUPER** power mode from the Ubuntu desktop top
  bar for maximum performance.
</Tip>

Verify Jetson Linux is running from your chosen storage:

```bash theme={null}
df -h /
```

You should see `/dev/nvme0n1p1` (NVMe) or `/dev/mmcblk0p1` (microSD) as the root filesystem.

<Accordion title="Legacy: JetPack 6.x SD-card image + NVMe migration">
  These steps apply **only to JetPack 6.x**, which still ships an SD-card
  `.img`. JetPack 7.2+ removed SD-card images — use the installer flow above
  instead.

  1. **Flash the SD card.** Download the JetPack 6.x Jetson Orin Nano Developer
     Kit SD-card image (`.zip` containing a `.img`) from the
     [JetPack SDK page](https://developer.nvidia.com/embedded/jetpack), write it
     to a microSD card with [Balena Etcher](https://etcher.balena.io/), and boot
     the Jetson from it to complete initial setup.

  2. **Install the NVMe SSD** into the M.2 Key M slot and confirm the Jetson
     sees it with `lsblk` (look for `nvme0n1`).

  3. **Flash JetPack to the NVMe** by streaming the SD-card image straight to the
     drive:

     ```bash theme={null}
     unzip -p jetson-orin-nano-devkit-super-SD-image_JP6.2.1.zip | \
         sudo dd of=/dev/nvme0n1 bs=4M status=progress conv=fsync
     ```

  4. **Update the boot configuration** so it points at the NVMe root. Note the
     PARTUUID (`ls -l /dev/disk/by-partuuid/ | grep nvme0n1p1`), then mount
     `/dev/nvme0n1p1` and change `root=/dev/mmcblk0p1` to
     `root=PARTUUID=<your-nvme0n1p1-uuid>` in
     `/boot/extlinux/extlinux.conf`.

  5. **Boot from NVMe.** Power off, remove the SD card, and power on. If it drops
     to the UEFI shell, set the NVMe as the first boot device under
     **Boot Manager → Boot Maintenance Manager → Boot Options**.

  6. **Expand the partition** to fill the disk:

     ```bash theme={null}
     sudo parted /dev/nvme0n1 resizepart 1 100%
     sudo resize2fs /dev/nvme0n1p1
     ```
</Accordion>

***

## Step 5: Configure headless operation

Switch the boot target from graphical desktop to console-only and enable SSH:

```bash theme={null}
sudo systemctl set-default multi-user.target
sudo systemctl enable ssh
```

***

## Step 6: Configure WiFi for headless boot

Make your saved WiFi connection available without a user session:

```bash theme={null}
sudo nmcli connection modify <your-wifi-name> connection.autoconnect yes
sudo nmcli connection modify <your-wifi-name> connection.autoconnect-priority 10
sudo nmcli connection modify <your-wifi-name> connection.permissions ""
```

The `connection.permissions ""` setting is critical — it removes the "only for this user" restriction so the connection activates on boot without anyone logged in.

***

## Step 7: Configure 5G modem failover (optional)

If you have a USB 5G modem attached, it may appear as a standard USB ethernet interface. Check with:

```bash theme={null}
nmcli device status
```

If you see it connected (e.g. as `usb2` with a `Wired connection` name), set it as a lower-priority fallback:

```bash theme={null}
sudo nmcli connection modify "Wired connection 2" connection.autoconnect yes
sudo nmcli connection modify "Wired connection 2" connection.autoconnect-priority 5
sudo nmcli connection modify "Wired connection 2" connection.permissions ""
```

Verify routing priorities:

```bash theme={null}
ip route
```

WiFi should have a lower metric (higher priority) than the modem. Traffic will automatically fall over to the modem if WiFi drops.

***

## Reboot and verify

```bash theme={null}
sudo reboot
```

Wait 30–60 seconds, then SSH in from another machine. If you connect successfully, the Jetson is running headless from NVMe with automatic network connectivity.

<Check>
  Your Jetson Orin Nano is now booting from NVMe, running headless, and
  connecting to the network on boot without manual intervention.
</Check>

## Step 8: Configure Ethernet for robot connections (if applicable)

If you are connecting the Jetson to a robot via Ethernet (e.g., a Unitree Go2), the robot's internal network uses a fixed subnet. Your Jetson's Ethernet interface needs a static IP on that subnet before the robot is reachable.

Find the Ethernet interface name:

```bash theme={null}
ip a
```

Look for the wired interface (typically `eth0` or `enP8p1s0`). Create a persistent connection with NetworkManager:

```bash theme={null}
sudo nmcli connection add type ethernet ifname <interface-name> \
  con-name robot-link ip4 192.168.123.11/24
sudo nmcli connection up robot-link
```

Replace `<interface-name>` with your actual interface and `192.168.123.11/24` with an IP appropriate for your robot's subnet (the Unitree Go2 uses `192.168.123.0/24`). This connection will activate automatically on boot.

Verify the robot is reachable:

```bash theme={null}
ping 192.168.123.161
```

<Note>
  Without this step, the Cyberwave driver will fail its preflight check with
  `none of [192.168.123.161, 192.168.12.1] responded on TCP port 9991` because
  the Ethernet interface has no IP in the robot's subnet.
</Note>

## Step 9: ML worker image (JetPack 7.2+)

<Warning>
  **STUB:** to be expanded and re-reviewed before publish.
</Warning>

Cyberwave ships a Jetson-native ML worker image (`cyberwaveos/edge-ml-worker:<channel>-jetson`). On any host where the Cyberwave edge stack detects Jetson hardware, it pulls the `-jetson` tag automatically — no configuration required.

The `-jetson` image runs on the entire Jetson Orin family (all `sm_87`), with GPU-accelerated YOLOE, YOLO-World, and ONNX Runtime out of the box. Ultralytics fused-attention ops execute on native sm\_87 kernels — no CPU fallback.

### Supported hardware

| Device                 | Memory     | YOLOE variants that fit             |
| ---------------------- | ---------- | ----------------------------------- |
| Orin Nano 4 GB         | 4 GB       | `yoloe-11n`                         |
| Orin Nano 8 GB         | 8 GB       | `yoloe-11n`, `yoloe-11s`            |
| Orin NX 8 GB           | 8 GB       | `yoloe-11n`, `yoloe-11s`            |
| Orin NX 16 GB          | 16 GB      | `yoloe-11n/s/m`, likely `yoloe-11l` |
| AGX Orin 32 GB / 64 GB | 32 / 64 GB | any YOLOE variant                   |

Larger models generally need more headroom for KV cache and CLIP text encoder activations; treat the table as a starting point.

### Requirements

* **JetPack 7.2 or later** (L4T r38+, Ubuntu 24.04) on any Jetson Orin SKU. The GPU-accelerated `-jetson` ML worker image requires JetPack 7.x — on JetPack 6.x hosts the edge stack still runs, but this worker falls back to CPU. The `-jetson` image ships CUDA 12.9 runtime libraries; the JetPack 7 CUDA 13.x driver (injected at container start) is backward-compatible with them.
* `nvidia-container-toolkit` installed and enabled (shipped by default with JetPack via `apt install nvidia-jetpack`).
* Roughly **\~9 GB** of free space for the first pull. The image bundles PyTorch, ONNX Runtime GPU, CUDA 12.9 runtime libraries, and cuDNN so the container works on any JetPack 7.2+ Orin host without needing CUDA installed system-wide.

<Note>
  Validated on **JetPack 7.2** (L4T r38, CUDA 13.2). The base edge stack runs on
  JetPack 6.x and 7.x, but the GPU-accelerated ML worker requires JetPack 7.x —
  so we strongly recommend flashing JetPack 7.2 or later.
</Note>

### What gets selected

On a Jetson host, the Cyberwave edge stack:

* Pulls the `-jetson` image variant automatically (instead of the base or GPU variant).
* Passes `--runtime nvidia -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=all` so the nvidia-container-runtime injects the Tegra iGPU device files and the CUDA driver into the container.
* Skips Hailo and discrete-GPU device passthrough.

If the `-jetson` tag can't be pulled (e.g. an ad-hoc branch build), the edge stack falls back to the base CPU tag and logs the demotion. Everything keeps running, just on CPU.

### Verifying it's using the Jetson image

After the edge service has been up for a minute or two:

```bash theme={null}
docker ps --format '{{.Image}}\t{{.Names}}' | grep edge-ml-worker
```

The image tag should end in `-jetson`. To confirm iGPU access from inside the container:

```bash theme={null}
docker exec "$(docker ps -q -f name=cyberwave-worker)" \
  python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
```

You should see `True` and a Tegra device name (for example `Orin`).

### Overriding the auto-selection

If you need to pin a specific tag (custom build, hot-fix loop), set `CYBERWAVE_WORKER_IMAGE` in `~/.cyberwave/credentials.json` and restart the edge service:

```json theme={null}
{
  "CYBERWAVE_WORKER_IMAGE": "cyberwaveos/edge-ml-worker:local"
}
```

Point at the un-suffixed tag — the edge stack still appends `-jetson` on Jetson hosts. If you're pointing at a private registry outside `cyberwaveos/edge-ml-worker:*`, include the `-jetson` suffix yourself.

### Troubleshooting

* **Worker starts on CPU instead of `-jetson`** — check that `/etc/nv_tegra_release` exists on the host and that `docker info` lists the `nvidia` runtime. If both are present but the edge stack still picks CPU, look for a `falling back to` warning in the edge service logs — the `-jetson` image probably failed to pull.
* **`torch.cuda.is_available()` returns False inside the worker** — the container is running on CPU. Confirm `NVIDIA_VISIBLE_DEVICES=all` is set in the container env (`docker inspect <container> | grep NVIDIA`), that the container was started with `--runtime nvidia`, and that `nvidia-smi` works from the host.
* **Out of memory when loading a YOLOE model** — the model is too large for this Jetson SKU. Consult the sizing table above; either downshift to a smaller variant (`yoloe-11n` / `yoloe-11s`) or move the workload to Orin NX / AGX.
* **Very slow first start** — the image is \~3–4 GB and Jetson NVMe throughput on first pull can be modest. Subsequent restarts reuse the local layer cache.
