Skip to content

CLI

The CLI is a thin client over the server API; it does not talk to Docker directly, so a server must be running.

KAIO_ADDR (default 127.0.0.1:8080) says which server it talks to. Point it elsewhere for one command with KAIO_ADDR=10.0.0.2:8080 kaio-cli status, or reach a node of the cluster with node <name> run.

In a development checkout, replace kaio-cli below with cargo run --bin kaio-cli -- or make cli ARGS="…".

Flag Effect
--json Emit machine-readable JSON instead of formatted tables
--version Print the CLI’s version and build SHA, then exit, without reaching the server
Terminal window
kaio-cli status # system summary
kaio-cli health # health check
kaio-cli version # this CLI's version, and the server's
kaio-cli events # recent history
kaio-cli events --follow # live SSE tail
kaio-cli tui # launch the interactive TUI

version prints the CLI’s own version first, with the git SHA it was built from in parentheses, then asks the server for its own, so a CLI left behind after an upgrade shows up as two numbers that disagree:

cli: 0.3.7 (2664602)
server: 0.3.7

The server line reads unreachable at <addr> when nothing answers there, and version --json reports that as "server": null, with the SHA on its own sha field. A build made outside a git checkout, and without KAIO_GIT_SHA set, stamps dev instead of a SHA.

Terminal window
kaio-cli container ls
kaio-cli container start <id>
kaio-cli container stop <id>
kaio-cli container restart <id>
kaio-cli container pause <id>
kaio-cli container unpause <id>
kaio-cli container rm <id>
kaio-cli container logs <id> [--follow] [--tail <n>]
kaio-cli container shell <id> # interactive shell, Ctrl-] to exit
Terminal window
kaio-cli stack ls
kaio-cli stack show <name>
kaio-cli stack deploy <name> <compose_file> [--env-file .env]
kaio-cli stack rm <name>
kaio-cli stack start <name>
kaio-cli stack pause <name>
kaio-cli stack unpause <name>
kaio-cli stack update <name>
kaio-cli stack restart <name>
kaio-cli stack logs <name> [--follow] [--tail <n>]
kaio-cli stack scan <name> # Trivy vulnerability scan
kaio-cli stack check-update <name>
kaio-cli stack versions <name>
kaio-cli stack rollback <name> <version>
kaio-cli stack version-rm <name> <version>
Terminal window
kaio-cli stack copy <name> --to prod-02 # definition only, not started
kaio-cli stack copy <name> --to prod-02 --with-volumes # carry the data across
kaio-cli stack copy <name> --as web-staging # clone it here under another name
kaio-cli stack copy <name> --to prod-02 --dry-run # show the plan, change nothing
kaio-cli stack copy <name> --as web-02 --publish 8091:9091 --start
kaio-cli stack copy <name> --as web-03 --port-offset 2000 --start
kaio-cli stack copy <name> --as web-02 --env SITE_URL=https://staging.example.net
kaio-cli stack copy <name> --as web-02 --env-file staging.env

A clone on the same host claims the ports its source already holds, so the copy is refused rather than written unusable (--allow-port-clash accepts it anyway). --publish old:new (repeatable) and --port-offset N rewrite only the ports: entries of the copy’s compose file; --env and --env-file override its variables. A port written as ${HTTP_PORT}:80 is left to the variables.

The source is never stopped, written to or removed, and a copy that fails part way is undone on the destination. --to and --from name nodes as kaio-cli node ls does; leaving one out means this server, so a copy that stays here needs a different name with --as.

Without --start the copy is written but not brought up: two live copies would contend for the same DNS, webhooks, queues and mail. --allow-binds accepts that mounted host paths stay behind, and --allow-anonymous-volumes accepts losing volumes Docker named itself. Both are refusals by default, because either one silently produces a copy that looks healthy and is missing data.

See the API route for what the plan contains and what stops a copy before it starts.

Saved as pending until stack apply. See Environment variables.

Terminal window
kaio-cli stack env ls <name> # secrets are masked
kaio-cli stack env set <name> TAG=1.27 DB_PASSWORD=s3cret [--secret]
kaio-cli stack env unset <name> TAG
kaio-cli stack env import <name> .env # merge a .env file
kaio-cli stack apply <name> # redeploy with the pending environment

Each of these three nouns exposes the same three commands:

Terminal window
kaio-cli image ls
kaio-cli image rm <id>
kaio-cli image prune
kaio-cli volume ls
kaio-cli volume rm <name>
kaio-cli volume prune
kaio-cli network ls
kaio-cli network rm <id>
kaio-cli network prune

See Multiple servers.

On the control plane:

Terminal window
kaio-cli cluster token # print the join token, minting one if there is none
kaio-cli cluster token --rotate # mint a new one; servers holding the old one must rejoin
kaio-cli cluster info # control plane? member? neither?

On the server joining it:

Terminal window
kaio-cli join <control-plane> --join-token <t> [--name <n>] [--advertise <host:port>]
kaio-cli join 10.0.0.1:8080 --token-stdin # read the token from stdin
kaio-cli cluster leave # leave: tells the control plane, then forgets

--name defaults to the joining server’s own host name; the control plane sanitises it and suffixes it if it is taken. --advertise is rarely needed: the server reports its own KAIO_ADDR, and the control plane supplies the address the request arrived from when that is a wildcard. Pass it when neither is what others reach the server at: behind NAT, or a reverse proxy.

The Kaio servers that have joined this one. See Multiple servers.

Terminal window
kaio-cli node ls # the cluster, with each node's last probe
kaio-cli node show <name>
kaio-cli node rm <name>
kaio-cli node check [<name>] # probe one node, or every node
kaio-cli node <name> run <command> # run any command on that node

node <name> run forwards through this server’s gateway, so every command works there: node prod-01 run stack ls, node prod-01 run health, even node prod-01 run tui. A node that is not in the registry has no route and answers Node '<name>' has not joined this cluster.

Names that would collide with the subcommands above (ls, show, rm, check, run, help) are never handed out: a host called run is enrolled as run-2.

Nodes enter this list by running kaio-cli join on them, and nothing here edits one: a server is the source of truth about its own address. node rm drops a member, which comes back by joining again.

Terminal window
kaio-cli system prune [--volumes]

--json turns any command into a data source:

Terminal window
# Stacks with a pending update
kaio-cli --json stack ls | jq '.[] | select(.update_available) | .name'
Terminal window
# Nodes that are not answering
kaio-cli --json node ls | jq -r '.[] | select(.status != "online") | "\(.name) \(.last_error)"'

A pipeline drives these same commands over HTTP, with the published image and its entrypoint overridden: see Deploy from CI.

Kaio, built by Régis Gaidot