Jetson Deployment
X-Edge AI runs on NVIDIA Jetson modules (arm64 / L4T) with the same three-service stack as x86 boxes — frontend, backend, and the Python inference service. What changes on Jetson is the inference image: it must be built against the JetPack-provided CUDA/TensorRT libraries, with an ONNX Runtime wheel that matches your JetPack version.
Supported profiles
Section titled “Supported profiles”| Profile | Device / JetPack | Base image | ONNX Runtime | Execution provider |
|---|---|---|---|---|
jetson | Orin / Xavier / Nano, JetPack 5+ (L4T r35+) | l4t-base:r35.4.1 | JetPack wheel (per device) | TensorRT → CUDA → CPU |
jetson-gpu | TX2, JetPack 4.5 (L4T r32.5) | l4t-base:r32.5.0 | onnxruntime-gpu 1.10.0 (cp36 wheel) | CUDA (TensorRT EP not available on JetPack 4.5) |
jetson-cpu | Any arm64 Jetson | python:3.11-slim (arm64) | onnxruntime (CPU) | CPU |
Two implications of the TX2 GPU profile worth knowing before you start:
- ONNX opset ceiling. ONNX Runtime 1.10 supports up to opset 15. Export
TX2-bound models with
--opset 15(the bundle seeds an opset-15 model for this reason). Newer Jetsons running current JetPack wheels accept opset 17. - Frozen Python 3.6 dependency set. JetPack 4.5 is locked to Python 3.6, so
the
jetson-gpuimage uses a pinned legacy dependency fork. Preferjetson(Orin/JetPack 5) for new fleets.
Prerequisites (on the Jetson)
Section titled “Prerequisites (on the Jetson)”Images are built natively on the device — cross-building L4T images under QEMU is unreliable and unsupported.
docker version # >= 20.10docker compose version # Compose v2 plugin requiredsudo apt-get install -y zstd openssldf -h ~ # keep a few GB free (TX2 eMMC is small)docker info | grep -i nvidia # GPU profiles: NVIDIA container runtime must be wired inFor GPU profiles, pick the ONNX Runtime wheel matching your JetPack version from
the Jetson Zoo — the x86
onnxruntime-gpu pip wheel does not work on Jetson (it links x86 CUDA libs;
“missing library” at startup is the classic symptom).
Build → bundle → install
Section titled “Build → bundle → install”flowchart LR A["build-release-images.sh\n(build + tag 3 images)"] --> B["build-release-bundle.sh\n(docker save → dist/<profile>-<version>/)"] B --> C["install.sh on the target\n(load images → compose up → seed admin)"] A -. "build host: Jetson, internet once" .-> B C -. "target host: air-gapped OK" .-> C
-
Build the images (on the build Jetson, internet required once):
Terminal window # GPU profile — pass the JetPack-matched ONNX Runtime wheelORT_WHEEL_URL="https://<jetson-zoo-wheel-for-your-jetpack>.whl" \./scripts/build-release-images.sh <version> --profile jetson-gpu# CPU profile — no wheel needed./scripts/build-release-images.sh <version> --profile jetson-cpuBefore the first GPU build, de-risk the wheel and GPU access with the probe script:
./scripts/spike-jetson-gpu.sh(GO/NO-GO check). -
Package the offline bundle:
Terminal window ./scripts/build-release-bundle.sh --profile jetson-gpu --version v<version>The output
dist/jetson-gpu-v<version>/contains the compressed image archive with an integrity manifest, a Tegra-correct compose file, the seed model, and theinstall.sh/update.sh/uninstall.shrunbook scripts — the same layout as the x86 bundle described in Offline Bundle Install. -
Install on the target device (copy the bundle directory via USB/scp):
Terminal window cd dist/jetson-gpu-v<version>sudo ./install.sh # auto-detects arm64 and accepts jetson-* bundles# sudo ./install.sh --with-systemd # optional: start on bootThe installer verifies the archive checksum, loads images, generates a unique JWT secret and admin password, starts the stack, waits for health, and prints the dashboard URL and admin password once — store it immediately.
-
Verify GPU inference is real:
Terminal window docker compose -f compose/docker-compose.release.yml ps # 3 services healthydocker inspect aiboard-inference-real --format '{{.HostConfig.Runtime}}' # expect: nvidiadocker logs aiboard-inference-real 2>&1 | grep -iE "CUDA EP|provider" # CUDA/TensorRT EP activecurl -s http://localhost:5000/api/system/metrics | grep -oE '"gpu"[^}]*}' # real GPU metrics
Tegra-specific behavior
Section titled “Tegra-specific behavior”The Jetson bundle compose differs from the x86 one on purpose:
runtime: nvidiaand root inside the inference container — required for GPU device access on Tegra./sysmount — GPU load and temperatures come from Tegra sysfs (there is nonvidia-smion Jetson), feeding the dashboard’s GPU panel.OPENBLAS_CORETYPEpin — avoids an OpenBLAS illegal-instruction crash on Tegra cores.- Per-field N/A metrics — dashboard health fields a Jetson cannot report show as N/A instead of fake zeros.
Execution-provider selection works the same as on x86:
EXECUTION_MODE=auto tries TensorRT → CUDA → CPU and logs a warning if it
degrades. For a hard guarantee that the box never silently falls back to CPU,
set EXECUTION_MODE=cuda (or tensorrt) plus STRICT_EP=1 — the service then
refuses to start instead of degrading. See
Execution provider fell back to CPU.
Versioning on the Jetson line
Section titled “Versioning on the Jetson line”Jetson releases are versioned by the VERSION file only — the Jetson line never
creates product v* tags. When you see version drift between the dashboard’s
About panel and your bundle, compare against the bundle manifest, not Git tags.
See Versions & Updates.
If something goes wrong
Section titled “If something goes wrong”| Symptom | Fix |
|---|---|
unknown shorthand flag: 'f' in -f | Compose v2 plugin missing — sudo apt-get install -y docker-compose-plugin |
Cannot autolaunch D-Bus during build | Headless credential-helper issue — the build script isolates it automatically; for ad-hoc docker commands use an empty DOCKER_CONFIG dir |
GPU container: no CUDA-capable device | Host is missing the NVIDIA container runtime, or the compose lacks runtime: nvidia (the bundle compose already sets it) |
| Inference exits with a missing-library error | Wrong ONNX Runtime wheel for your JetPack — pick the matching wheel from the Jetson Zoo and rebuild |
install.sh: “profile does not match bundle” | Pass --profile jetson-gpu (or jetson-cpu) explicitly — both are valid on arm64 |
| Model rejected at upload (TX2) | Model exported with opset > 15 — re-export with --opset 15 for ONNX Runtime 1.10 |