HELP
nodex CLI
Every command, flag and file the nodex CLI reads when it pushes a release.
The nodex CLI
nodex runs on your own computer or in CI. It turns your code into a release for a fleet: it builds or pulls every service's image, uploads it to the Nodexa registry and marks the release complete. Every device in the fleet then updates to it, unless the device or fleet is pinned to another release.
This is not the nodex command on a device. That one only talks to the device's local supervisor; see Debugging.
Commands
nodex push [flags] # build, push and release
nodex --version # installed version
nodex push --help # every flag, with examplespush is the only command. Everything else is set with the flags below, a manifest file, or environment variables.
Install
| System | How |
|---|---|
| macOS, Linux (x86_64 or ARM64) | The install script. It downloads the latest release to /usr/local/bin, asking for sudo if needed |
| Windows (x86_64) | Download nodex_windows_amd64.tar.gz from the latest GitHub release and unpack it into a folder on your PATH |
| Anything with Go | go install, or build from source |
macOS and Linux
curl -fsSL https://raw.githubusercontent.com/adhuldas/nodexa-cli/main/install.sh | sh
nodex --versionWindows (PowerShell)
curl.exe -fsSL -o nodex.tar.gz https://github.com/adhuldas/nodexa-cli/releases/latest/download/nodex_windows_amd64.tar.gz
$null = mkdir -Force $HOME\bin
tar -xzf nodex.tar.gz -C $HOME\bin
& $HOME\bin\nodex.exe --versionAdd $HOME\bin to your PATH to run it as plain nodex.
With Go
go install github.com/adhuldas/nodexa-cli/cmd/nodex@latestFrom source
git clone https://github.com/adhuldas/nodexa-cli.git
cd nodexa-cli
go build -o /usr/local/bin/nodex ./cmd/nodex Pushing containers needs Docker on the same machine. Multi-platform builds also need docker buildx. Pushing ESP32 WebAssembly modules doesn't need Docker.
Authentication
Every push needs a Fleet ID (copy it from the fleet's summary card) and a deployment token (the fleet's Deployment Token button). Pass them as flags or environment variables; flags win when both are set.
| Environment variable | Same as | Notes |
|---|---|---|
NODEXA_FLEET_ID | --fleet-id | Required |
NODEXA_API_TOKEN | --token | Required. Keep it in a secret store, never in the repository |
NODEXA_PLATFORM | --platform | Optional target CPU for every service |
In CI
# Any CI system: store both values as secrets, then
export NODEXA_FLEET_ID="$FLEET_ID"
export NODEXA_API_TOKEN="$DEPLOY_TOKEN"
export NODEXA_PLATFORM="linux/arm64" # optional
nodex pushnodex push flags
| Flag | Default | What it does |
|---|---|---|
--fleet-id <id> | $NODEXA_FLEET_ID | Fleet that gets the release. Required |
--token <token> | $NODEXA_API_TOKEN | Fleet deployment token. Required |
-f, --file <path> | nodexa.yml | Manifest to read. Also accepts a compose file |
-c, --compose-file <path> | none | Compose file to read. Takes priority over -f |
-s, --service <name> | folder name | Service name for a lone Dockerfile; with a compose file, pushes only that service |
--dockerfile <path> | Dockerfile | Dockerfile to build when there is no manifest or compose file |
--context <dir> | . | Build context for that Dockerfile |
-p, --platform <list> | $NODEXA_PLATFORM | Target CPU, e.g. linux/arm64. Comma-separate several to build one multi-platform image |
-r, --registry-file <path> | auto-detected | Credentials for private registries you pull from |
-h, --help | Show help and examples |
How nodex finds your services
It uses the first of these that applies, and prints which one it picked:
- A compose file given with
-c - A file given with
-f nodexa.ymlornodexa.yamlin the current folderdocker-compose.yml,docker-compose.yaml,compose.ymlorcompose.yamlSkipped if it has no services nodex can build or pull.
- A
Dockerfilein the current folder (or the--dockerfilepath)One service, named with
--serviceor else after the folder: lowercased, anything other than letters, digits,-and_turned into-, up to 63 characters. --serviceand--dockerfiletogether, even from another folder
If none apply, the push stops with a message listing these options.
nodexa.yml reference
Use a manifest for several services, pre-built images, or ESP32 modules.
platform: linux/arm64 # optional default for every service
services:
- name: web # required; lowercase, used as the service name on devices
dockerfile: Dockerfile # default: Dockerfile
context: . # default: .
- name: worker
dockerfile: worker/Dockerfile
context: worker/
platform: linux/arm/v7 # overrides the top-level platform
- name: firmware
image: ghcr.io/my-org/firmware:v1.2.0 # pre-built: pulled, re-tagged, pushed
- name: sensor
wasm: build/sensor.wasm # ESP32 only; can't be combined with image or dockerfile| Field | Required | Meaning |
|---|---|---|
platform | No | Default target CPU for every service |
services[].name | Yes | Service name. Must be unique in the file |
services[].dockerfile | No | Dockerfile to build. Default Dockerfile |
services[].context | No | Build context. Default . |
services[].image | No | Existing image to pull, re-tag and push instead of building |
services[].platform | No | Target CPU for this service only |
services[].wasm | No | Path to a built .wasm module for ESP32 fleets. Can't be combined with image or dockerfile |
A file whose services: is a map instead of a list is read as a compose file, even when passed with -f.
docker-compose.yml support
Only the parts that decide what to push are read: build (a path, or context, dockerfile and platform), image and platform. Ports, volumes, environment and the rest are ignored.
services:
api:
build: # built from source
context: ./api
dockerfile: Dockerfile.prod
platform: linux/arm64
web:
build: ./web # short form: just the context
cache:
image: redis:7-alpine # pre-built: pulled, re-tagged, pushed
worker: {} # no build or image: uses ./worker/Dockerfile if present- A service with
buildis built from source. - A service with only
imageis pulled and pushed as-is. - A service with neither uses
<service-name>/Dockerfileif it exists. - If a service is still left without a Dockerfile and a
Dockerfilesits next to the compose file, that service is built from it. nodex prefers the service named with-s, then the one named after the folder.
Target platform
Build for the CPU your devices have. From highest priority:
--platformorNODEXA_PLATFORM: replaces the platform of every service- The service's own
platform(innodexa.yml, or in composebuildor the service) - The top-level
platforminnodexa.yml - Otherwise, your computer's own CPU
| Devices | Platform |
|---|---|
| Raspberry Pi 4, Variscite DART, 64-bit ARM boards | linux/arm64 |
| BeagleBone Black and other 32-bit ARM boards | linux/arm/v7 |
| Nodex Nomad and other x86_64 PCs | linux/amd64 |
A comma-separated list (for example linux/arm64,linux/arm/v7) builds one multi-platform image with docker buildx, so one release serves mixed fleets.
Private registries
If an image or base image lives in a private registry (GitHub, Docker Hub, AWS ECR, GitLab), nodex logs in first using a credentials file. It looks in the current folder for registry.yml, registries.yml, .registry.yml or .registries.yml (or .yaml), or takes a path from -r.
# Format 1: keyed by registry host (recommended)
ghcr.io:
username: my-github-username
password: ghp_personalAccessTokenWithReadPackages
docker.io:
username: my-docker-username
password: dckr_pat_secretToken
# Format 2: the same map under "registries:" or "auths:"
registries:
ghcr.io:
username: my-github-username
password: ghp_personalAccessTokenWithReadPackages
# Format 3: a list
- registry: ghcr.io
username: my-github-username
password: ghp_personalAccessTokenWithReadPackagesNever commit this file. Add it to .gitignore:
echo "registry*.yml" >> .gitignoreWhat a push does
- Logs into any private registries from the credentials file
- Finds the services, as described above
- Reserves the next revision number for the fleet
- Logs into the Nodexa registry with your token (skipped when only WebAssembly modules are pushed)
- For each service, builds or pulls the image, pushes it and records its digest
WebAssembly modules are checked and uploaded directly, without Docker.
- Marks the release complete; devices start updating on their next check-in
If any step fails after the revision is reserved, the release is marked failed and devices keep running what they had. Fix the problem and push again; the next push gets a new revision.
Example output
🔍 compose file: docker-compose.yml
Found 2 service(s): [api web]
📝 Reserving release for fleet <fleet-id>...
Reserved revision 17
🔑 Logging into registry nodexa.elzora.tech...
🔨 Building and pushing 2 service(s)...
✅ api → sha256:…
✅ web → sha256:…
✅ Completing release revision 17...
Release 17 is now complete
🎉 Push complete!Examples
# Folder with a Dockerfile; service name taken from the folder
nodex push --fleet-id <fleet-id> --token <api-token>
# Same, but name the service
nodex push --fleet-id <fleet-id> --token <api-token> --service backend
# Dockerfile somewhere else, with its own build context
nodex push --fleet-id <fleet-id> --token <api-token> -s api --dockerfile docker/api.Dockerfile --context .
# A compose file, or just one service from it
nodex push --fleet-id <fleet-id> --token <api-token> -c docker-compose.yml
nodex push --fleet-id <fleet-id> --token <api-token> -c docker-compose.yml -s api
# A manifest at a custom path
nodex push --fleet-id <fleet-id> --token <api-token> -f deploy/staging.yml
# 32-bit ARM boards (BeagleBone), or one image for several CPUs
nodex push --fleet-id <fleet-id> --token <api-token> --platform linux/arm/v7
nodex push --fleet-id <fleet-id> --token <api-token> --platform linux/arm64,linux/arm/v7
# Private base images
nodex push --fleet-id <fleet-id> --token <api-token> -r ~/.config/nodexa/registry.ymlError messages
no nodexa.yml manifest, docker-compose.yml, or Dockerfile foundRun from your project folder, or point nodex at the files with
-c,-f, or--servicewith--dockerfile.required flag(s) "fleet-id", "token" not setPass
--fleet-idand--token, or setNODEXA_FLEET_IDandNODEXA_API_TOKEN.reserving release: HTTP 401orHTTP 403The token is wrong, revoked, or for another fleet. Generate a new one from the fleet's Deployment Token dialog and check the Fleet ID.
service[0].name is requiredorat least one service is requiredEvery entry under
services:innodexa.ymlneeds aname, and the list can't be empty.wasm can't be combined with image or dockerfileA WebAssembly service only takes
nameandwasm.… is not a WebAssembly moduleThe
wasmpath points at something else, such as a source file or a native binary. Build with the command under Deploy apps → ESP32 and point at the.wasmoutput.login to external registry … failedCheck the username and password in the credentials file. GitHub needs a token with
read:packages.exec format erroron the deviceThe image was built for the wrong CPU. Push again with the right
--platformfrom the table above.
