Skip to main content

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:

FlagEffect
--helpShow help for the command
--versionPrint CLI version (on the root lium command)

Pods​

Day-to-day pod lifecycle, access, and observation.

CommandPurpose
lium lsList available GPU nodes
lium upCreate a new pod
lium psList your active pods
lium describeShow one pod in full: ports, GPU, template, billing
lium auditWho 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 sshOpen an SSH session
lium execRun a command on a pod
lium scpCopy files to/from a pod
lium rsyncSync directories to a pod
lium cpCopy files between two pods directly over SSH
lium rmStop / remove pods
lium rebootReboot one or more pods
lium logsStream or tail pod logs
lium updateUpdate a running pod (e.g. install Jupyter)
lium port-forwardForward a local port to one of a pod's exposed ports
lium schedulesList / cancel auto-termination schedules

Clusters​

Several whole machines on one InfiniBand or RoCE fabric, rented as one cluster.

CommandPurpose
lium clustersList fabrics, rent a cluster, show its members and hostfile, remove it

Storage​

Persistent volumes and pod backups.

CommandPurpose
lium volumesManage persistent volumes (list / new / rm)
lium bkConfigure, run, inspect, cancel, delete, and restore pod backups

Templates​

CommandPurpose
lium templatesList and search Docker templates and images

Account & Config​

CommandPurpose
lium signupCreate an account from the terminal and store its API key
lium initFirst-time setup for an existing account (API key + SSH key)
lium configRead / write CLI configuration
lium themeChange CLI color theme
lium completionPrint or install shell tab completion for bash, fish or zsh
lium whoamiAPI key in use, account, balance, SSH key and CLI version
lium balanceShow your current account balance
lium spendHourly burn, estimated spend per active pod, and runway
lium billingWhat the ledger charged, per pod, with optional date and API-key filters
lium fundFund your account with TAO or free alpha from your wallet
lium topupTop up your balance with a stablecoin (agent self-funding)
lium ssh-keysList / sync SSH keys on your account

Providers​

Commands for users running provider nodes. See also the Providers section for setup and architecture.

CommandPurpose
lium mineBootstrap a provider node end-to-end
lium gpu-splittingPrepare Docker storage for multi-tenant rental
lium providerFull 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)​

Since lium 0.0.38 (lium#124)

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 with confirmation_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_key with no value) return their default or fail with input_required — pass the value as an option;
  • lium init takes the --no-browser path, and a missing API key is reported as an error that names LIUM_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"}
  • pod has the same keys as one row of lium ps --format json, so one parser serves both.
  • termination_time is there only when --ttl, --until or --budget set one: the time sent to the backend.
  • With --jupyter, pod.jupyter_url is the URL the install produced.
  • On the failures below, after the rent went through, the envelope's data names the pod that now bills: {"pod_id": "…", "pod_name": "…"}. Codes: pod_not_ready (exit 1), pod_start_failed (exit 3), gpu_count_mismatch (exit 1, and data.pod_removed is true when --strict-gpus removed the pod), gpu_verification_failed (exit 1), jupyter_install_failed (exit 1), and any API error (server_error, rate_limited, …) during the wait, the --ttl retry or the Jupyter install. A failure before the rent created no pod; its data, when there is one, carries the server's request_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_rent included) names it in data.volume_id and data.volume_huid. A failure at the rent itself (api_timeout, rent_rejected or the server's own code, server_error, exit 3; insufficient_balance, permission_denied, exit 6) names no pod: the CLI does not know whether one exists. Before you retry, run lium 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 --in or --at the list key is scheduled, and termination_time holds the scheduled time.
  • spent_usd is an estimate: uptime × the pod's price per hour (0 for a pod still PENDING, null when either is unknown).
  • lium rm --all --json on an account with no pods prints {"removed": [], "failed": []} and exits 0.
  • When some of the pods could not be removed, stdout still holds the document and the command exits 1: removed lists the pods that went, failed the huids that did not, so a retry targets only failed.

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​

CodeMeaning
0Success
1General error
2Configuration error — bad arguments, unreadable script, missing configuration; also a prompt that could not be shown (confirmation_required, input_required, since 0.0.38)
3API error — the API refused or failed the call
4SSH error
5Pod not found
6Permission denied — the account is not allowed to do this

Two exceptions:

  • lium exec exits with the remote command's exit code, so lium exec my-pod "cmd" && next-step chains the way you expect.
  • lium provider … has its own exit-code map — the same numbers carry different meanings there (2 is auth, 3 a portal error, 5 an SSH error, 6 missing configuration), and it also emits 7, 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