Skip to content

Offline Bundle Install

This is how you install on a customer edge site with no internet, no registry access, and no image building on the box. You install everything from a single self-contained release bundle that ships on USB media.

A release bundle is one directory, named for its profile and version, for example jetson-gpu-v1.58.4. The jetson-gpu profile targets the Jetson TX2 on JetPack 4.5 and runs inference on the GPU through CUDA.

Inside the bundle:

  • Directoryjetson-gpu-v1.58.4/
    • Directoryimages/
      • jetson-gpu-v1.58.4.images.tar.zst the three service images, compressed
      • manifest.txt profile, version, image tags, checksum
    • Directorycompose/
      • docker-compose.release.yml image-based stack (no build step)
      • docker-compose.tls.yml HTTPS overlay (opt-in)
      • docker-compose.certs.yml datasource TLS cert overlay (opt-in, see below)
      • .env.template port and setting placeholders
    • Directorymodels/
      • predictive_maintenance_op15.onnx demo seed model (random weights)
    • Directorylib/ installer helpers
      • …
    • Directorysystemd/ boot-start unit template
      • …
    • Directorycerts/
      • …
    • Directorytls/
      • …
    • install.sh
    • update.sh
    • uninstall.sh
    • compact-db.sh reclaims database disk space
    • README.txt

The installer copies what the running stack needs into /opt/aiboard: the compose files, the seed model, the generated .env, tls/, certs/, and the pre-update backups/. After a successful install or update, the bundle directory is no longer needed by the running box.

The target Jetson must already have these installed. They ship on the golden JetPack image. The installer checks for them and stops if anything is missing; it does not install them for you.

  • Docker Engine 20.10 or newer, with the docker compose v2 plugin
  • zstd and openssl
  • The NVIDIA container runtime, registered with Docker
  • About 6 GB of free disk where you copy the bundle
  1. Copy the whole bundle directory from your USB media to the Jetson, then open a terminal in it:

    Terminal window
    cd jetson-gpu-v1.58.4/
  2. The installer detects the Jetson, loads the container images, generates secrets, brings the stack up, and waits for health.

    Terminal window
    sudo ./install.sh

    Useful options:

    Terminal window
    sudo ./install.sh --profile jetson-gpu # force the profile
    sudo ./install.sh --with-systemd # also start on boot
    sudo ./install.sh --no-backup # skip the pre-migration snapshot (see below)

    Under the hood install.sh runs these steps in order:

    1. Preflight — checks the CPU architecture, Docker, Compose, zstd, openssl, the Jetson and its NVIDIA container runtime, and free disk.
    2. Verify the image archive checksum. This detects corrupt or tampered media.
    3. Load images — docker load from the bundled archive. No pull, no build.
    4. Stage the runtime files into /opt/aiboard.
    5. Generate secrets — creates the JWT signing secret and admin password on the box and writes the secret to /opt/aiboard/.env (mode 600). No secret ever ships inside the bundle.
    6. Snapshot the database — if the box already holds a database, the installer backs it up before the new release can migrate it. See Database snapshots.
    7. Start the stack — docker compose up -d.
    8. Health gate — waits for the services to report healthy.
    9. Seed the admin and print the dashboard URL and admin password once.
  3. The installer generates the JWT signing secret automatically. You normally never touch it, but you can confirm the protected .env exists:

    Terminal window
    sudo ls -l /opt/aiboard/.env # expect mode -rw------- (600)

    If a corporate policy requires your own value, set it in that file, then re-run sudo ./update.sh from the bundle you installed to apply it. Every signed-in session ends when the secret changes.

    # /opt/aiboard/.env (placeholder — generate a strong random value, do not reuse)
    AIBOARD_JWT_SECRET=your-strong-random-jwt-secret
  4. The bundle ships a demo model with random weights. Replace it with your trained model so predictions are meaningful. Models for the TX2 must use ONNX opset 15 or lower.

    • Upload from the dashboard (recommended). See Deploying Models.
    • Before first install, you can overwrite models/predictive_maintenance_op15.onnx in the bundle. Every update refreshes the seed model from the new bundle, so use the dashboard for a lasting change.
  5. Confirm the services are up:

    Terminal window
    docker ps --filter name=aiboard- --format 'table {{.Names}}\t{{.Status}}'

    Expect aiboard-inference-real and aiboard-backend-real to show (healthy), and aiboard-frontend-real to show Up. Then open the dashboard URL the installer printed and sign in with the admin account.

Deploy a newer bundle. Copy it to the box, open a terminal in it, and run:

Terminal window
sudo ./update.sh

update.sh reads the live configuration from /opt/aiboard/.env, so you do not copy anything between bundle directories. It then:

  1. Snapshots the database with its writers stopped, and verifies the archive. See Database snapshots.
  2. Loads the new images and points the configuration at them.
  3. Recreates the stack. The backend migrates the database on start.
  4. Waits for health. If the stack does not become healthy, the update stops, prints how to roll back, and removes nothing.
  5. Cleans up, only after the new release is healthy:
    • It keeps the newest AIBOARD_BACKUP_KEEP snapshot archives (default 3) and removes older ones. Set AIBOARD_BACKUP_KEEP=0 in /opt/aiboard/.env to keep every archive. See Environment Variables.
    • It keeps the images of the release it just installed and the release it replaced, and removes older Xisom images. When you re-run the same release, for example after a failed health gate, it skips image cleanup so the previous release stays available for rollback.

To skip the snapshot, pass --no-backup. Only do this if you took a snapshot by other means: database migrations are forward-only.

Both install.sh and update.sh back up the database before a new release can migrate it:

  • The snapshot is taken with the database’s writers stopped, so the archive is consistent. The stack starts again when the run continues; if the run fails before that point, the stopped containers are restarted.
  • The archive is read back before anything else changes. If it does not read back, the run stops while the current database is still untouched.
  • Archives are written to /opt/aiboard/backups/ as backup-<UTC timestamp>.tgz. The run prints the exact restore command next to the archive name.
  • A snapshot covers the database only, not uploaded models. Back up the model volume separately if you need to roll models back.
  • --no-backup skips the snapshot on either script.

Datasource-side TLS — the CA, client certificate, and key an MQTT or OPC UA datasource uses to reach a secured broker or server — is separate from the box’s own HTTPS certificate. It is off by default. Turn it on with a .env flag, symmetric with AIBOARD_TLS_ENABLED:

/opt/aiboard/.env
AIBOARD_CERTS_ENABLED=1

Put your MQTT/OPC UA CA and client certificate/key files in /opt/aiboard/certs/. When the flag is on, the installer and update.sh layer the bundled docker-compose.certs.yml overlay on top of the release stack, and the backend mounts that directory read-only. Before the first install, set the flag in the bundle’s compose/.env.template instead. On a running box, re-run sudo ./update.sh after you change the flag.

SymptomWhat it means
Bundle manifest missingYou ran the installer from a source folder, not an assembled bundle. Use the bundle directory you copied from USB.
Architecture mismatchThe bundle is for an arm64 Jetson, and this machine is not one. Copy the bundle to the Jetson.
/etc/nv_tegra_release is absentThe machine is not a Jetson running L4T. Install on the Jetson TX2.
NVIDIA container runtime not wired into DockerDocker on this Jetson has no nvidia runtime. Fix the JetPack image so Docker lists it.
Low diskNot enough free space for the images. Free about 6 GB and retry.
Inference shows startingCUDA initialisation takes up to about 90 seconds on first start — wait. If it then turns unhealthy, check docker logs aiboard-inference-real.
Found an existing install at: …The box was installed before /opt/aiboard existed. Run the same script once with --migrate.
Archive checksum mismatchCorrupted or tampered USB media. Re-copy the bundle and retry.

See the full Troubleshooting runbook for symptom-keyed fixes.