CLI Reference
Reference for the lium commands, grouped by what you're trying to do. Each command has its own page with arguments, options, and runnable examples.
If you're new, start at the Quickstart and circle back here once you know which command you need.
Global Options
Available on every command:
| Flag | Effect |
|---|---|
--help | Show help for the command |
--version | Print CLI version (on the root lium command) |
Pods
Day-to-day pod lifecycle, access, and observation.
| Command | Purpose |
|---|---|
lium ls | List available GPU nodes |
lium up | Create a new pod |
lium ps | List your active pods |
lium describe | Show one pod in full: ports, GPU, template, billing |
lium audit | Who did what to the account's pods, and when — every rent, reboot, edit and delete with the key or session behind it (since 0.0.37) |
lium ssh | Open an SSH session |
lium exec | Run a command on a pod |
lium scp | Copy files to/from a pod |
lium rsync | Sync directories to a pod |
lium cp | Copy files between two pods directly over SSH |
lium rm | Stop / remove pods |
lium reboot | Reboot one or more pods |
lium logs | Stream or tail pod logs |
lium update | Update a running pod (e.g. install Jupyter) |
lium port-forward | Forward a local port to one of a pod's exposed ports |
lium schedules | List / cancel auto-termination schedules |
Clusters
Several whole machines on one InfiniBand or RoCE fabric, rented as one cluster.
| Command | Purpose |
|---|---|
lium clusters | List fabrics, rent a cluster, show its members and hostfile, remove it |
Storage
Persistent volumes and pod backups.
| Command | Purpose |
|---|---|
lium volumes | Manage persistent volumes (list / new / rm) |
lium bk | Configure, run, inspect, cancel, delete, and restore pod backups |
Templates
| Command | Purpose |
|---|---|
lium templates | List and search Docker templates and images |
Account & Config
| Command | Purpose |
|---|---|
lium signup | Create an account from the terminal and store its API key |
lium init | First-time setup for an existing account (API key + SSH key) |
lium config | Read / write CLI configuration |
lium theme | Change CLI color theme |
lium completion | Print or install shell tab completion for bash, fish or zsh |
lium whoami | API key in use, account, balance, SSH key and CLI version |
lium balance | Show your current account balance |
lium spend | Hourly burn, estimated spend per active pod, and runway |
lium billing | What the ledger charged, per pod, with optional date and API-key filters |
lium fund | Fund your account with TAO or free alpha from your wallet |
lium topup | Top up your balance with a stablecoin (agent self-funding) |
lium ssh-keys | List / sync SSH keys on your account |
Providers
Commands for users running provider nodes. See also the Providers section for setup and architecture.
| Command | Purpose |
|---|---|
lium mine | Bootstrap a provider node end-to-end |
lium gpu-splitting | Prepare Docker storage for multi-tenant rental |
lium provider | Full provider-portal automation: portal session, node lifecycle, central-miner-server config, sync, billing, and machine-request queries |
Patterns That Work Across Commands
Pod selectors
Most pod-targeting commands (exec, scp, rsync, rm, reboot) accept any of the forms below; ssh takes one pod, by name or index:
- A pod name:
lium ssh my-pod - An index from
lium ps:lium ssh 1 - A comma-separated list:
lium exec 1,2,3 "uptime" - The keyword
all:lium scp all ./config.json
A target that matches no pod at all exits 5 — a typo must not read like work
that happened. In a comma-separated list, a name that matches nothing is skipped
and the other pods are acted on. Asking for everything is the opposite case: lium rm --all and
lium reboot --all on an account with no pods leave the asked-for state already
true, so they warn and exit 0.
Output formats
Since 0.0.39 ls, ps, balance, describe and templates all accept both --format json and the shorthand --json, print the same thing either way, and a failure the command handles (an API error, a pod that does not exist, a rejected key) is a JSON envelope on stderr with stdout empty, so a script can jq stdout without guarding against error text. Usage errors the argument parser catches — an unknown option, a missing argument — still exit 2 with click's plain-text message before the command runs. Before 0.0.39, ls and ps took only --format json, balance and describe only --json, and templates had no JSON output.
lium ls --format json | jq '.[] | .gpu_type'
lium ps --json > pods.json # since 0.0.39; lium ps --format json on earlier versions
lium templates --json | jq -r '.[] | "\(.id) \(.name)"' # the ids lium up --template_id takes — since 0.0.39
Scripts and agents (non-interactive use)
LIUM_NONINTERACTIVE, the exit-2 confirmation_required / input_required failures and config reset --yes shipped in lium 0.0.38 (lium#124, released 9 Sep 2026); everything on this page marked lium#124 describes 0.0.38 and later. On 0.0.37 and earlier, when stdin is not a terminal the CLI still opens the prompt (and hangs) on most confirmations, and rm <index> / rm --all skip theirs and remove without asking.
The renter commands never wait on a prompt they cannot show (since 0.0.38, lium#124; mine, gpu-splitting and provider still prompt directly, and fund still asks for an encrypted coldkey's password — see below). When stdin is not a terminal, or LIUM_NONINTERACTIVE=1 is set, a command that would have asked a question either takes its documented default or fails immediately with exit code 2 and a hint naming the flag to pass:
- confirmations (
up,rm,volumes rm,bk rm|restore|cancel|delete|restore-cancel,config reset) fail withconfirmation_required— pass--yes/-y.rm <name>asks nothing on a terminal; without one it refuses only since 0.7.0 (lium#246), and 0.6.0 and earlier remove the pod without asking; - prompts for a value (
fund's wallet, amount and hotkey;config set api.api_keywith no value) return their default or fail withinput_required— pass the value as an option; lium inittakes the--no-browserpath, and a missing API key is reported as an error that namesLIUM_API_KEY; no browser is opened.
export LIUM_API_KEY=... # no browser login is attempted without a terminal
lium up --gpu H100 -y --no-ssh # -y: rent without the confirmation prompt
lium rm my-pod -y # -y on every destructive command
lium fund -w default -a 1.5 -y # values that would be prompted for must be passed as options
One prompt in fund is not lium's: when the coldkey is encrypted, lium fund has to unlock it before signing, and bittensor-wallet asks Enter your password: on the controlling terminal, so -y, LIUM_NONINTERACTIVE and a redirected stdin do not skip it — with a terminal the command waits for the password; without one (cron, CI, an agent's sandbox) the unlock fails and the TAO path exits with coldkey_unlock_failed (exit 2). For scripts, use a coldkey saved without a password; bittensor-wallet's BT_PW_<KEYFILE PATH> variable is not a plain-password switch (it holds the encoded value its own save_password_to_env() writes).
lium up --json and lium rm --json (since lium 0.6.0)
lium up and lium rm accept --json too (for rm it is --format json). Stdout then holds one JSON document. up sends its progress lines (the node picked, the rent, the wait) and its confirmation prompt to stderr; rm asks its questions (--all, a row number) on stdout. A failure the command handles leaves stdout empty, and stderr holds the error envelope: {"ok": false, "error": {"code", "message", "hint", "exit_code"}, "data"?}. The one exception is rm failing on some of its pods (below).
Pair --json with --yes. When stdin is not a terminal, or LIUM_NONINTERACTIVE=1 is set, the CLI does not ask, so a command that would prompt fails with confirmation_required (exit 2) before it rents or removes anything. Piping only stdout (lium up --json | jq) from a terminal still asks.
lium up … --json ends once the pod is ready. It does not open an SSH session or stream logs (it implies --no-ssh). The example is abridged: "…" stands for the other keys of the row.
{"pod": {"id": "<pod id>", "huid": "zesty-fox-ab", "name": "train", "status": "RUNNING", "gpu_type": "L40", "gpu_count": 1, "price_per_hour": 0.33, "ssh_cmd": "ssh root@<ip> -p 40298", "ports": {"22": 40298}, "template": "Pytorch (Cuda + DinD)", "jupyter_url": null, "removal_scheduled_at": "2026-09-12T03:21:09.175903", "…": "…"}, "termination_time": "2026-09-12T03:21:09.175903+00:00"}
podhas the same keys as one row oflium ps --format json, so one parser serves both.termination_timeis there only when--ttl,--untilor--budgetset one: the time sent to the backend.- With
--jupyter,pod.jupyter_urlis the URL the install produced. - On the failures below, after the rent went through, the envelope's
datanames the pod that now bills:{"pod_id": "…", "pod_name": "…"}. Codes:pod_not_ready(exit1),pod_start_failed(exit3),gpu_count_mismatch(exit1, anddata.pod_removedistruewhen--strict-gpusremoved the pod),gpu_verification_failed(exit1),jupyter_install_failed(exit1), and any API error (server_error,rate_limited, …) during the wait, the--ttlretry or the Jupyter install. A failure before the rent created no pod; itsdata, when there is one, carries the server'srequest_id. Exception:--volume new:…creates the volume before the rent, and every later failure keeps it; a failure up to and at the rent (timeout_before_rentincluded) names it indata.volume_idanddata.volume_huid. A failure at the rent itself (api_timeout,rent_rejectedor the server's own code,server_error, exit3;insufficient_balance,permission_denied, exit6) names no pod: the CLI does not know whether one exists. Before you retry, runlium ps.
lium rm … --json:
{"removed": [{"id": "<pod id>", "huid": "zesty-fox-ab", "name": "train", "uptime": "2.0h", "uptime_hours": 2.0, "price_per_hour": 0.33, "spent_usd": 0.66, "spent_is_estimate": true}], "failed": []}
- With
--inor--atthe list key isscheduled, andtermination_timeholds the scheduled time. spent_usdis an estimate: uptime × the pod's price per hour (0for a pod stillPENDING,nullwhen either is unknown).lium rm --all --jsonon an account with no pods prints{"removed": [], "failed": []}and exits0.- When some of the pods could not be removed, stdout still holds the document and the command exits
1:removedlists the pods that went,failedthe huids that did not, so a retry targets onlyfailed.
In a shell script:
set -eo pipefail
pod=$(lium up --gpu H100 -y --ttl 2h --json | jq -r .pod.huid)
lium exec "$pod" --json "nvidia-smi -L"
lium rm "$pod" -y --json
Environment variables
Override config without editing files:
LIUM_API_KEY=xyz lium ls
For provider commands, LIUM_PROVIDER_COLDKEY and LIUM_PROVIDER_HOTKEY set the wallet identity (or persist them via lium config set provider.coldkey ... / lium config set provider.hotkey ...). LIUM_PORTAL_URL overrides the portal base URL, and LIUM_PROVIDER_ACK=1 auto-confirms the persona gate on spend-affecting lium provider … subcommands. See lium provider for the full surface.
LIUM_NONINTERACTIVE=1 makes every renter command behave as if stdin were not a terminal: a prompt takes its documented default or fails with exit code 2 (see Scripts and agents above).
Exit codes
| Code | Meaning |
|---|---|
0 | Success |
1 | General error |
2 | Configuration error — bad arguments, unreadable script, missing configuration; also a prompt that could not be shown (confirmation_required, input_required, since 0.0.38) |
3 | API error — the API refused or failed the call |
4 | SSH error |
5 | Pod not found |
6 | Permission denied — the account is not allowed to do this |
Two exceptions:
lium execexits with the remote command's exit code, solium exec my-pod "cmd" && next-stepchains the way you expect.lium provider …has its own exit-code map — the same numbers carry different meanings there (2is auth,3a portal error,5an SSH error,6missing configuration), and it also emits7, which this table does not list.
In machine mode (--format json, --json) a handled failure prints the JSON envelope on stderr and its error.exit_code repeats the process exit status. lium up --json and lium rm --json (since lium 0.6.0) use the same table: confirmation_required is 2, pod_start_failed is 3, a pod that matches no target is 5.
See also
- Quick Start — first pod in 5 minutes
- Installation — install and authenticate
- SDK — Python client used under the hood
- API — REST endpoints the CLI calls