STEP 1 ยท ๐ฅ๏ธ HOST
Server deployment โ this page
limit-docs :3010 ยท limit-dash :80 (UI) + :9000/udp (ingest)
โ
STEP 2 ยท ๐ค ROBOT
Robot deployment
limit-robot on the Jetson AGX Thor ยท aarch64
โ Robot Deploy โ the other half of this stack, on the Jetson AGX Thor
Server deployment ๐ฅ๏ธ host x86-64
The host (Server2, 192.168.0.83) runs two containers โ the docs site and the dash (UI + relay). No database or backend services in the telemetry pipeline itself โ anything else on the box (e.g. a SQL server on :1433) is separate infrastructure, not part of this stack.
Before you start
- Two machines on the same L2 network โ the host and the robot (Jetson AGX Thor). No internet needed at runtime.
- Docker Engine installed and
sudo-free for the deploy user on both machines. - Images:
production-limit-docs-dockerandproduction-limit-dash-docker(both x86-64) on the host;production-limit-robot-docker(aarch64) on the Jetson. All three are untagged โ no:1.0tag exists. - Ports free on the host: 3010 (docs, published โ on this fleet :3000 is taken by Gitea), 80 (dash UI, via
--net host), 9000/udp (telemetry in from the robot), 8000 (relay API, localhost only).
โ Architecture rule: x86-64 images run only on the host,
production-limit-robot-docker (aarch64) only on the Jetson. docker load of the wrong image on the wrong machine fails immediately โ and the only thing that crosses the wire is UDP JSON telemetry (robot โ host, port 9000). The stack is observe-only: there is no command channel in either direction.Air-gapped image transfer
bash ยท save โ transport โ load
# 1. on the machine that BUILT the image docker save production-limit-dash-docker -o limit-dash.tar # 2. copy limit-dash.tar to the target (USB stick, sftp, whatever) # 3. on the TARGET machine docker load -i limit-dash.tar docker images | grep production-limit # confirm names + arch before you run
Docs site (this site, port 3010)
bash ยท docs
docker run -d \ --name limit-docs \ --restart unless-stopped \ -p 3010:3000 \ production-limit-docs-docker
- Static site โ the nginx container (Alpine) is the whole thing. No database, no backend; nginx listens on
:3000inside the container and is published to 3010 on this fleet because :3000 is used by Gitea on the host. Any free port works โ-p 3000:3000is fine elsewhere. - Pages:
index,robot,robot_model,drive,robot_deploy, andserver_deployโ this one โ plus the bundled offline ZED 2i SDK reference under/offline_docs/.
Limit Dash (host port 80)
bash ยท dash
docker run -d \ --name limit-dash \ --restart unless-stopped \ --net host \ production-limit-dash-docker
- One container, three parts: nginx serves the UI on host port :80 and proxies
/api/to the C++ relay on :8000 (SSE, buffering off, 60 s read timeout); the relay ingests UDP JSON on :9000/udp.--net hostis required โ the relay binds the machine's own:9000/:8000directly. - Relay endpoints:
GET /api/snapshot,GET /api/stream(SSE),GET /api/health. - Live tiles fed by the robot stream: T04 lidar, T05L/T05R front cameras (
/limitx/cam2), T06L/T06R rear cameras (/limitx/cam1), T08 IMU WIP, T09 GNSS, T12 source health, T28 system. 4 status LEDs (ENGINE, DOG LINK, MUSIC, DASH) and a SIM mode for no-robot testing. The remaining tiles (joints, battery, terrain, โฆ) read NO SOURCE until their producers exist.
Verify
curl http://192.168.0.83/โ dashboard HTML.curl http://192.168.0.83/api/healthโ relay reports alive.curl -I http://192.168.0.83:3010/โ the docs site (this one) answers 200.- Open
http://192.168.0.83โ tiles are grey/stale until the robot starts; flip SIM and they light up with synthetic data, proving the pipeline.
Day-to-day
| Task | Command |
|---|---|
| follow the logs | docker logs -f limit-dash (or limit-docs) |
| restart a piece | docker restart limit-dash |
| change the dash UI port | nginx listens on :80 via host networking; to move it, run bridge-mode instead: docker run -d --name limit-dash -p 8080:80 -p 9000:9000/udp production-limit-dash-docker |
| stop the host stack | docker stop limit-dash limit-docs |
โฆStop order: robot first, then the host stack. The robot is the producer and holds the physical state; the dashboard can drop off any time. See the Robot Deploy page for the other side.