CLI
The miabi CLI is the imperative command-line client for Miabi. Almost all of it is a pure
consumer of the public HTTP API — the same API the web console uses — so anything you can do in the
console you can script from a terminal or a CI pipeline. Its headline job is the deploy flow:
miabi apps deploy web --tag "$GIT_SHA" --wait
This deploys the app at the given image tag, blocks until the deployment reaches a terminal state, and exits non-zero if it failed — exactly what you want in CI.
The CLI is a standalone tool released on its own cadence, separate from the Miabi server. Source, releases, and the authoritative command reference live on GitHub: github.com/miabi-io/cli.
Install
Homebrew (macOS and Linux):
brew install miabi-io/tap/miabi
Homebrew 6 requires third-party taps to be trusted. The one-liner above handles it; if you tap first,
run brew trust miabi-io/tap before brew install miabi.
Go:
go install github.com/miabi-io/cli@latest
mv "$(go env GOPATH)/bin/cli" "$(go env GOPATH)/bin/miabi" # Go names the binary after the module: `cli`
Or grab a prebuilt binary for Linux, macOS, or Windows from the releases page.
Docker — no install at all, which is handy in CI:
docker run --rm -e MIABI_SERVER -e MIABI_TOKEN miabi/cli:latest whoami
# deploy from a pipeline — exits non-zero if the rollout fails
docker run --rm -e MIABI_SERVER -e MIABI_TOKEN \
miabi/cli:latest apps deploy web --tag "$GIT_SHA" --wait
Authenticate
The CLI talks to your instance with an API token. Provide the URL and token by environment variable — ideal for CI:
export MIABI_SERVER="https://miabi.example.com"
export MIABI_TOKEN="mb_…" # created in the console: Settings → API tokens
miabi whoami # verify: shows the identity, scopes, and active workspace/app
Or log in once for interactive use. miabi login stores the URL + token in ~/.miabi/config.yaml
(mode 0600):
miabi login --server https://miabi.example.com # opens the browser to sign in (password or SSO)
miabi login --no-browser # no local callback: paste a token from /request-token
miabi --token "$MIABI_TOKEN" login # non-interactive: validate and save this token
By default login opens the console's /cli/authorize page and captures the token through a
one-time code sent to a local callback — nothing to copy and paste. When a token is already set with
--token or MIABI_TOKEN, it validates and saves that instead; --web forces the browser flow
anyway. Without --server, login re-authenticates the current context's server.
Narrowing what the token may do
--scopes limits the token to read, write, deploy, or any combination — the default grants
all three. The scope rides through the sign-in, including an SSO one, so the browser hands back a
token that is already narrowed:
miabi login --context prod-ro --scopes read # a context you can only read from
miabi login --scopes deploy # ship, but not edit
A login token can never carry admin or *; the server refuses to mint one, and the CLI rejects
those names before opening the browser. If the server is older than --scopes it ignores the
request — the CLI checks what it was actually given and refuses to save a token broader than you
asked for, rather than leaving you with a false assurance.
A read-only context is what makes miabi mcp read-only in fact and not only in
the tools it advertises.
Configuration resolves in the order flags → environment (MIABI_SERVER, MIABI_TOKEN) → config
file. Point at a different file with MIABI_CONFIG. --url and MIABI_URL are the deprecated
spellings of --server / MIABI_SERVER; both still work.
For CI, mint a least-privilege deploy token bound to one workspace and one app (scope deploy).
It can deploy that app and nothing else. Use broader read/write tokens for interactive work. See
API tokens.
Multiple servers
Each login is saved as a named context — server URL, TLS trust, token, and the bound
workspace/app — named after the server's hostname unless you pass --context <name>.
miabi context ls # all contexts; the arrow marks the current one (alias: ctx)
miabi context current # print the current context's name
miabi context use staging # switch
miabi context delete staging # remove (alias: rm)
miabi --context prod apps ls # target one context for a single command
MIABI_CONTEXT does the same as --context. For a server with a private CA, pass
--certificate-authority <bundle> (or MIABI_CA); login saves the bundle path with the context.
Context: workspace and app
Most commands act on an app in a workspace. Rather than repeating those every time, set them once as
part of the current context in ~/.miabi/config.yaml:
miabi workspace list # name, display name, role (alias: ws)
miabi workspace switch acme # set the active workspace
miabi workspace show # the currently active workspace
miabi use web # bind a default app for app-scoped commands
miabi use # show the current binding
miabi use --clear # unbind
With an app bound, app commands need no argument (miabi apps deploy targets the bound app). The
binding is per workspace and is cleared when you switch workspaces. You can always override
per-command with a positional app argument or --workspace/-w. A workspace-bound token already
targets its workspace, so it needs no workspace switch.
Commands
App-scoped commands live under apps (alias of app) and take the app as their first
argument, or use the app bound with miabi use.
| Command | What it does |
|---|---|
miabi login [--no-browser | --web] [--scopes read,write,deploy] · miabi whoami | Sign in and save a context; show identity, scopes, and active context. |
miabi context ls | current | use | delete | List, show, switch, or remove saved contexts (alias: ctx). |
miabi workspace ls | show | switch <name-or-id> | List, show, or set the active workspace (alias: ws). |
miabi use [app] | --clear | Bind (or show/clear) the default app. |
miabi apps ls | List the workspace's applications (marks the bound app). |
miabi apps create <name> (--image <img> [--tag] | --git-repo <url> [--git-ref] [--build-method]) [--port] [--node <id>] [--use] | Create an app from an image or Git source. --build-method is auto, dockerfile, or buildpack. |
miabi apps deploy [app] [--tag <tag>] [--strategy <s>] [--no-cache] [--wait] [--timeout 10m] | Deploy; --wait blocks and exits non-zero on failure. --no-cache rebuilds every layer (Git-source apps). |
miabi apps start | stop | restart [app] | Control the app's container. |
miabi apps rollback [app] (--to <version> | --to-previous) [--yes] | Roll back to a previous release. |
miabi apps status [app] [--deployment <n>] | Show app / deployment status. |
miabi apps logs [app] [--follow] [--tail N] [--deployment <n>] | Runtime logs (or a deployment's build logs with --deployment); --follow streams. |
miabi apps deployments [app] | Deploy history — the NUMBER column addresses a deployment. |
miabi apps releases [app] | List an app's releases (by VERSION). |
miabi apps env ls [app] | List the app's env vars (secret values are masked). |
miabi apps env set [app] KEY=VALUE [--secret] | Set an environment variable. |
miabi apps env set [app] KEY --from-file <f> [--secret] | Set the value from a file (or - for stdin) — keeps it out of your shell history. |
miabi apps env import [app] --from-file .env [--secret] | Bulk-import variables from a file (- = stdin). |
miabi apps set-source [app] (--image <img> [--tag] [--registry-id <id>] | --git-repo <url> | --git-repository-id <id>) [--git-ref] [--build-method] [--builder] [--yes] | Switch the app between a prebuilt image and a Git build, or edit its current source. Keeps domains, env, volumes and history. --git-repository-id uses a saved repository and its credentials; --yes skips the prompt when switching source type. |
miabi apps resync-pipeline [app] | Reload the repository's pipelines.yaml — adopts one if the app has none, re-syncs it if it does. |
miabi apps invalidate-cache [app] | Start a new build-cache generation, so the next build rebuilds every layer. |
miabi apps rm [app] [--yes] | Delete an application. |
miabi pipeline ls | List the workspace's pipelines. |
miabi pipeline run <pipeline> [--branch <ref>] [--commit <sha>] [--no-cache] | Start a run (pipeline by name, UID, or id). |
miabi pipeline rerun <run-id> [--no-cache] | Run the same pipeline again at the ref and commit that run built. |
miabi volumes … | Manage volumes, their files, and their backups (see below). |
miabi db … | Manage database instances + logical databases (see below). |
miabi secrets … | Manage the workspace secret vault (see below). |
miabi configs … | Manage the workspace's configuration files (see below). |
miabi apply -f <file|dir> [--prune] [--dry-run] | Converge to declarative manifests. A directory reads every .yaml and .yml file under it, the same files a Git source pointed at it reads. |
miabi delete -f <file> [--dry-run] | Delete exactly the resources a manifest bundle names. |
miabi completion <shell> | Shell completion (tab-completes app slugs). |
Databases
Managed instances (PostgreSQL, MySQL, MariaDB, Redis, MongoDB, libSQL) and the logical databases on them. Instances are addressed by slug (or numeric id).
miabi db ls # list instances
miabi db engines # engines + default versions
miabi db create shop --engine postgres [--version 16] [--size-mb 2048] [--node <id>]
miabi db get shop
miabi db start | stop | restart shop
miabi db logs shop [--follow] [--tail 200]
miabi db credentials shop # reveal admin connection (admin)
miabi db upgrade shop --to 17 [--stop-apps]
miabi db rm shop [--yes]
# logical databases on an instance:
miabi db databases shop # list
miabi db databases create shop app_prod [--app web] # optionally attach to an app
miabi db databases connection shop app_prod # reveal connection (admin)
miabi db databases rm shop app_prod [--yes]
# link to an app (injects DB_URL, DB_HOST, … ; Redis injects REDIS_*):
miabi db link shop app_prod --app web [--prefix ANALYTICS]
miabi db link shop app_prod --app web --env url=SPRING_DATASOURCE_URL --env database_url=
miabi db link cache --app web # Redis: link the whole instance
miabi db links --app web # what is linked, and the vars it injects
miabi db unlink shop app_prod --app web
Volumes
Persistent volumes, addressed by name (alias: vol).
miabi volumes ls
miabi volumes get web-data # details and the apps mounting it
miabi volumes create web-data [--size-mb 5120] [--node <id>]
miabi volumes create shared --driver nfs --driver-opt device=:/export --driver-opt o=addr=10.0.0.5,rw
miabi volumes attach web-data --path /var/lib/data [--app web]
miabi volumes detach web-data [--app web] # unmount; the data is kept
miabi volumes storage # workspace usage: declared vs measured
miabi volumes rm web-data [--yes] # refused while an app mounts it
--driver is local (the default: node-local, one node mounts it), nfs or cifs (shared storage
every replica can mount), or host (an operator-managed path, privileged workspaces only). Driver
options are write-only; pass anything holding a password with --driver-opt-file key=path (- reads
stdin) to keep it out of your shell history.
Files inside a volume — one side of cp is <volume>:<path>, and - is stdin/stdout:
miabi volumes ls-files web-data [--path uploads]
miabi volumes cp ./nginx.conf web-data:/conf/nginx.conf # upload (overwrites)
miabi volumes cp web-data:/dump.sql - | gzip > dump.sql.gz # download; --force overwrites a local file
miabi volumes rm-file web-data uploads [--yes] # a directory is removed recursively
The panel rejects uploads over 512 MiB, and listings stop at 5000 entries.
Backups go to the workspace's S3 backup target; without one every backup command is refused. Backups are addressed by the numeric ID the listing shows.
miabi volumes backups web-data # list
miabi volumes backups run web-data [--wait] [--timeout 30m]
miabi volumes backups restore web-data 42 [--yes] # overwrites the volume's data
miabi volumes backups logs web-data 42
miabi volumes backups rm web-data 42 [--yes]
restore does not stop the apps mounting the volume — stop them first unless the workload tolerates
its files changing underneath it.
Secrets
The workspace secret vault: values encrypted at rest, write-only over the
API, referenced from an app's env as ${{ secrets.NAME }}. Addressed by name. Supply a value
with --from-file (or stdin) rather than --value, which lands in your shell history.
miabi secrets ls # list (no values)
miabi secrets get API_KEY # description, version, timestamps
miabi secrets set API_KEY --from-file api.key # create, or rotate if it exists
miabi secrets set API_KEY --description "Stripe live key" # update only the description
cat api.key | miabi secrets set API_KEY --from-file -
miabi secrets reveal API_KEY # print the value (admin; audited)
miabi secrets usage API_KEY # apps referencing it
miabi secrets rm API_KEY [--yes]
seal encrypts a value locally to the workspace's public key and prints a kind: SealedSecret
manifest you can commit. See Sealed secrets.
echo -n "$API_KEY" | miabi secrets seal API_KEY >> secrets.yaml
miabi secrets seal API_KEY --from-file api.key --raw # only the sealed value
miabi secrets seal-key > .miabi/sealing.pub # the public key, safe to commit
miabi secrets seal API_KEY --from-file api.key --public-key-file .miabi/sealing.pub # offline
miabi secrets seal -f secrets.yaml --in-place # turn every plaintext Secret into a SealedSecret
Configs
The workspace's configuration files — file sets mounted into applications
read-only, encrypted at rest. Addressed by name; files come from disk with --from-file
([key=]path, repeatable) or --from-dir. --mode sets the default octal file mode (e.g. 0644),
--delimiters "<<,>>" changes the interpolation delimiters for files whose own syntax uses {{ }},
and --description labels the config.
miabi configs ls # names, file counts, size, version
miabi configs get prom-conf # files and digest (no content)
miabi configs set prom-conf --from-file prometheus.yml # create, or replace the file set
miabi configs set prom-conf --from-file rules/alerts.yml --merge # patch one file, keep the rest
miabi configs set nginx --from-dir ./conf.d --recursive
miabi configs set app --from-file app.conf=- < app.conf
miabi configs cat prom-conf prometheus.yml # print one file
miabi configs edit prom-conf prometheus.yml # edit in $EDITOR
miabi configs usage prom-conf # apps mounting it
miabi configs rm prom-conf [--yes]
set replaces the whole file set unless you pass --merge, so it stays idempotent in CI. Saving
redeploys every app mounting the config (unless it sets reloadPolicy: none) — --yes skips the
confirmation. Reading content (cat, edit, and --merge) is admin-only and audited; a config
marked --sensitive additionally requires --reveal to print.
Mounting a config into an app is declarative — see the manifest reference.
AI agents
miabi mcp runs a Model Context Protocol server, so an AI agent
(Claude Desktop, Claude Code, Cursor, …) can inspect and operate your panel. Each tool call becomes
one authenticated API request, so the agent inherits your token, workspace, and RBAC — no model runs
inside miabi.
claude mcp add miabi -- miabi mcp # read-only
claude mcp add miabi -- miabi mcp --allow-write # also deploy, restart, roll back
See AI Agents (MCP) for the tool catalog, transports, and how to scope what an agent is allowed to do.
Common flags
--wait— block on the deploy and exit non-zero when it endsfailed(CI gating).-o, --output table|json|yaml(or the--jsonshorthand) — machine-readable output forjq/yq-friendly scripting; human tables otherwise.-w, --workspace <name-or-id>— override the active/bound workspace for one command.--context <name>— use a saved context for one command (envMIABI_CONTEXT).--certificate-authority <file>— trust a CA bundle for the server's certificate (envMIABI_CA).--insecure-skip-tls-verify— skip certificate verification (envMIABI_INSECURE_SKIP_TLS_VERIFY). The CLI warns on every command that uses it; prefer--certificate-authority.--no-color— plain output (also auto-disabled off a TTY or whenNO_COLORorMIABI_NO_COLORis set).--verbose— log every HTTP request to stderr.--version— print the CLI version.
Managing the host
One group of commands is the exception to "pure API client": setup, upgrade and stack … act
on the machine they run on, through its Docker socket, and never touch the HTTP API. They are what
installs and operates a Miabi host — see
Installation and Upgrades.
| Command | What it does |
|---|---|
miabi setup [-d, --domain <host>] [--version <x.y.z>] [--image <ref>] [--yes] | Install the stack on this host, or converge an existing install. Idempotent. A fresh install defaults to the latest published release. |
miabi upgrade [component] [--version <x.y.z>] [--image <ref>] [--yes] | Roll the stack forward, rolling back automatically if the new image never becomes healthy. --version swaps only the tag (keeping a private registry); --image replaces the whole reference. Also available as miabi stack upgrade. |
miabi stack status | What is running, its health, and any drift from the manifest. |
miabi stack apply [--yes] | Bring the running stack in line with the manifest: lists the components whose spec changed, then recreates only those. Nothing changed means nothing restarts. |
miabi stack restart [component] [--yes] | Restart in place so containers re-read their on-disk config. A restart does not apply manifest changes — use stack apply. |
miabi stack uninstall [--volumes] [--yes] | Remove the stack's containers; --volumes also destroys the database. |
miabi stack migrate-config | Rename the legacy /etc/miabi/stack.yaml to /etc/miabi/miabi.yaml. |
miabi stack env ls / get KEY | Show the environment the manifest carries. --gateway reads the gateway's instead of the control plane's. |
miabi stack env set KEY=VALUE […] [--apply] | Write variables to the manifest and show what changes. The running stack keeps the old values until miabi stack apply; --apply does both in one step. |
miabi stack env unset KEY […] [--apply] | Remove variables. A variable Miabi seeds (TZ, MIABI_LOG_LEVEL) comes back at its default rather than disappearing, and the output says so. |
Edits are saved, not applied, so several of them cost one recreate per component:
sudo miabi stack env set MIABI_SMTP_HOST=smtp.example.com MIABI_SMTP_PORT=587
sudo miabi stack env set GOMA_LOG_LEVEL=debug --gateway
sudo miabi stack apply
stack env is also reachable as miabi setup env … — the same command under the verb an operator
reaches for when configuring an install. Settings the manifest models with their own field (the
registry, the networks, the backup destination) are refused there, and the error names where the
value actually lives.
They all need root (they write /etc/miabi and use the Docker socket) and a Linux or macOS
Docker host. -f, --file points at a manifest other than /etc/miabi/miabi.yaml; so do
MIABI_CONFIG_FILE and the older MIABI_STACK_FILE.
setup also takes --admin-email, --acme-email, --control-url, --registry,
--registry-host, --subnet, --internal-subnet, --gateway-image, --runner-image,
--goma-config, and --no-host-proc. Each one overwrites the matching manifest field when given
and leaves it alone otherwise — see Install options.
A fresh setup, and upgrade with no component, install the latest published Miabi release,
resolved from versions.miabi.io when the command runs (GitHub is the fallback) — the CLI does not
carry a platform version, because it releases on its own cadence. Re-running setup on an existing
install keeps the image its manifest pins. MIABI_RELEASE_API points that lookup at a mirror for a
host with no route out, and --version/--image skip it entirely.
On upgrade, --version accepts 1.8.0 or v1.8.0 and swaps only the tag on the component's
current reference, so a private registry and non-Miabi components (the gateway) keep working;
--image replaces the reference outright. On setup, --version sets the control-plane image to
miabi/miabi:<version>, replacing a private-registry reference — pass --image there, or use
upgrade. Pinning matters: a floating tag such as latest warns, because a failed rollout has no
distinct previous image to roll back to. See Upgrades.
In CI/CD
The CLI is the recommended way to deploy from a pipeline. Set MIABI_SERVER and a deploy token as
MIABI_TOKEN, then call apps deploy --wait:
- name: Deploy to Miabi
run: |
docker run --rm -e MIABI_SERVER -e MIABI_TOKEN \
miabi/cli:latest apps deploy web --tag "${{ github.sha }}" --wait
env:
MIABI_SERVER: ${{ vars.MIABI_SERVER }}
MIABI_TOKEN: ${{ secrets.MIABI_DEPLOY_TOKEN }}
Because --wait exits non-zero on a failed rollout, a broken deploy fails the pipeline step. (A
prebuilt release binary works the same way if the
runner has no Docker.)
On GitHub Actions you do not need any of that — miabi-io/deploy-action
installs the CLI and runs the same command:
- uses: miabi-io/deploy-action@v1
with:
app: web
tag: ${{ github.sha }}
server: ${{ vars.MIABI_SERVER }}
token: ${{ secrets.MIABI_TOKEN }}
If you'd rather not install anything, the same deploy is one HTTP call. The {workspace} segment is
a real workspace — its numeric id, its UID, or its handle. There is no current alias on the API
(the CLI resolves that client-side):
curl -fsS -X POST "$MIABI_SERVER/api/v1/workspaces/acme/apps/web/deploy" \
-H "Authorization: Bearer $MIABI_TOKEN" \
-d '{"tag":"'"$GIT_SHA"'"}'
The request body accepts registry_id, tag, strategy, and no_cache (rebuild every layer, for
Git-source apps). There is no wait field and no
Idempotency-Key header — the server does not de-duplicate retried deploys. Poll the deployment's
status to gate a pipeline on the rollout.
Related
- AI agents (MCP) — expose the panel to Claude, Cursor, or any MCP client via
miabi mcp. - API tokens — create the token the CLI authenticates with.
- GitOps — the declarative counterpart driven by
miabi apply/miabi delete. - GitHub Actions — the official action that wraps
miabi apps deploy. - Volumes — the storage managed by
miabi volumes. - Secrets — the vault managed by
miabi secrets. - Configuration files — the file sets managed by
miabi configs. - Pipelines and Deploy on push — in-platform build/deploy automation.