CLI

The cloud CLI

The cloud CLI is a single binary for Linux, macOS and Windows. It talks to the same API the console uses, so anything you can do in the web dashboard you can also do from the terminal. This is especially useful for automation, CI/CD pipelines, and tasks you do not want to repeat by hand.

Install

# Linux and macOS
curl -fsSL https://cloud.co.zm/install.sh | sh

# Windows (PowerShell)
irm https://cloud.co.zm/install.ps1 | iex

Both installers are served from cloud.co.zm, detect your OS and architecture automatically, and verify the download's checksum before installing. After installation, cloud version prints what you have and cloud update fetches the latest release.

Sign in

cloud login

The terminal shows a short code and opens a browser page where you are already signed in. Check the code matches and approve. This works over SSH — open the URL on any device. The CLI gets an ordinary seven-day session; no password ever touches the terminal.

For CI and scripts, use an API token instead:

echo "$TOKEN" | cloud login --token-stdin     # or simply export CLOUD_TOKEN=...
cloud whoami                                   # who, which API, which organisation, when the credential expires
cloud logout

Organisation, region, contexts

Commands act within an organisation. Pick a default once and every command after that just works:

cloud org list
cloud org use acme
cloud region list

A context bundles an API URL, an organisation and a default region under a name. This lets you work across multiple environments without switching flags:

cloud context set prod --org acme --region zm-lsk-1 --use
cloud context set staging --org acme --region zm-lsk-1
cloud context list
cloud context use prod

Precedence, highest first: flags (--org, --region, --api-url) → environment variables (CLOUD_ORG, CLOUD_REGION, CLOUD_API_URL, CLOUD_TOKEN) → current context → built-in defaults. Credentials are stored per API host, so signing in to one does not disturb another.

Reading the commands

Every command follows the same pattern: cloud <resource> <verb> [name] [flags]. The name comes first, flags after:

cloud app domain add web shop.example.co.zm
#         ^      ^    ^   ^
#    resource  verb  name  flag-like arg

--help on any command lists its arguments, flags, and examples. When in doubt, cloud <resource> --help is your friend.

Common workflows

Here are a few patterns you will use often:

Deploy and verify

cloud app deploy web --image ghcr.io/acme/web:1.4.3 --wait
cloud app logs web --tail 20

--wait prints each status change as it happens and finishes once the app is ready or failed. The --tail shows the most recent log lines so you can confirm the new version started cleanly.

Check what is running

cloud app list -o json | jq '.[] | {name: .name, status: .status}'
cloud db list
cloud storage bucket list

Get credentials for a database

eval "$(cloud db credentials orders --format env)"
psql

--format env prints shell variable assignments that eval loads into the current shell. The next command (like psql) picks them up automatically.

Schedule a backup

cloud db backup enable orders --retention 7d

Sync a directory to S3

cloud storage sync ./public s3://assets-abc123/public --delete

--delete removes files from S3 that are no longer in the local directory. Add --dry-run first to see what would change without actually doing it.

Apps

cloud app list
cloud app create <name> --image <ref> [--port 8080] [--min 0] [--max 3] [--size <tier>]
                          [--env KEY=VALUE ...] [--region <name>] [--wait]
cloud app get <name>
cloud app deploy <name> --image <ref> [--wait]
cloud app scale <name> --min N --max N
cloud app logs <name> [-f] [--tail 200]
cloud app delete <name> [--yes]
cloud app domain list <name>
cloud app domain add <name> <hostname>
cloud app domain remove <name> <hostname>

Private registries: --registry-server, --registry-username and --registry-password-stdin together; the password is read from stdin, never from the command line.

Databases

cloud db engines
cloud db list
cloud db create <name> --engine postgresql [--version 17] [--size <tier>] [--wait]
cloud db get <name>
cloud db credentials <name> [--format env|url]
cloud db start|stop|restart <name>
cloud db backup enable <name> [--retention 3d]
cloud db backup create <name>
cloud db backup list <name>
cloud db restore <name> --to <new-name> [--at 2026-09-04T10:15:00Z]
cloud db delete <name> [--yes]

Object storage

cloud storage bucket list
cloud storage bucket create <name> [--region <name>]
cloud storage bucket get <name>
cloud storage bucket credentials <name> --format env|aws-profile|rclone
cloud storage bucket delete <name> [--yes]

cloud storage ls   [s3://bucket[/prefix]] [--recursive]
cloud storage cp   <src> <dst> [--recursive]      # local ↔ s3://, s3:// ↔ s3://
cloud storage sync <src> <dst> [--delete] [--dry-run]
cloud storage mv | rm | cat | stat
cloud storage presign s3://bucket/key [--method GET|PUT] [--expires 1h]

Object operations talk directly to the storage endpoint with the bucket's credentials; bytes never pass through the platform.

Virtual machines

cloud vm list
cloud vm create <name> --image <ref> [--size <tier>] [--region zm-lsk-1]
                       [--description <text>] [--ssh-key <key or path>] [--wait]
cloud vm get <name>
cloud vm start|stop|restart <name> [--wait]
cloud vm console <name>                 # opens the browser console
cloud vm ip get <name>
cloud vm ip attach <name>               # dedicated public IP (owner or admin)
cloud vm ip detach <name> [--yes]
cloud vm ssh-key list <name>
cloud vm ssh-key add <name> <path-or-key> [--label <text>]   # recorded; installed only on first boot
cloud vm ssh-key remove <name> <key-id> [--yes]
cloud vm delete <name> [--yes]

The machine starts as provisioning; --wait blocks until it is running or has failed. A dedicated IP takes effect the next time the machine starts. The username and a chosen password are set from the console's create page; see Virtual machines.

DNS

cloud dns list                                    # the organisation's domains
cloud dns records list <domain>
cloud dns records add <domain> --name <n> --type <t> --content <v> [--ttl 300] [--priority N]
cloud dns records update <domain> <record-id> [--name] [--type] [--content] [--ttl] [--priority]
cloud dns records remove <domain> <record-id> [--yes]

Types: A, AAAA, CNAME, MX, TXT, NS, SRV, CAA and PTR. --name is relative to the domain (@ for the apex). TTL is 60–86400 seconds (default 300, or 3600 for MX, TXT, NS and CAA); --priority is required for MX and SRV. A record is saved only once the name servers have accepted it. See Domains & DNS.

Output and scripting

  • --output table|json|yaml (-o) on every command; JSON is the full resource object — useful with tools like jq.
  • --quiet (-q) prints identifiers only, one per line — for piping into other commands.
  • --wait on create and deploy polls until the resource is ready, stopped or failed, printing each status change. If nothing changes because the platform worker is not running, it says so instead of timing out silently.
  • Destructive commands ask you to type the resource's name as a safety net; --yes skips the prompt. Without a terminal and without --yes, destructive commands refuse to run — this prevents accidental deletes in pipelines.

Exit codes

CodeMeaning
0success
1the operation failed (network error, or the platform reported an error)
2usage: bad flags, invalid input, or a conflict such as a name already taken
3not signed in, or the credential was rejected or has expired
4signed in, but your role does not allow this action
5the resource does not exist (or belongs to another organisation)

Error messages are the platform's own sentences — the same ones you would see in the console. This means you can copy-paste an error into a search or share it with support without losing context.

Troubleshooting

  • "not signed in" (exit 3) — run cloud login again, or check that your CLOUD_TOKEN environment variable is set correctly. cloud whoami prints who you are signed in as and when the credential expires.
  • "permission denied" (exit 4) — your role in the organisation does not allow this action. Ask an owner or admin to upgrade your role.
  • "resource not found" (exit 5) — the resource does not exist, or you are in the wrong organisation. Check with cloud org list and cloud org use <slug>.
  • "name already taken" (exit 2) — someone else (or you, in another org) already has a resource with that name. Pick a different one.
  • Command hangs — a network issue or a platform outage. Check status. Use --output json to get structured output even on failure.