CLI reference
This page documents every Ring CLI subcommand. Run ring <command> --help for the canonical list of flags on your installed version.
Global
ring --help
Print the list of subcommands.
ring --version
Print the installed Ring version.
Global options
--context / -c
Use a specific context from config.toml.
ring --context production deployment list ring -c staging deployment list
--config
Load a specific config.toml instead of the default $RING_CONFIG_DIR/config.toml. Useful for keeping several explicitly-named files (config_dev.toml, config_prod.toml) and switching between them. Overrides the RING_CONFIG_FILE environment variable.
This overrides only the config file: auth.json, Cloud Hypervisor firmware, and other assets still come from $RING_CONFIG_DIR. If the given file does not exist, Ring logs an error rather than silently falling back to the default.
ring --config ~/.config/kemeter/ring/config_dev.toml deployment list ring --config /etc/ring/config.toml server start
System
ring init
Initialize the Ring config directory: write config.toml and a freshly generated RING_SECRET_KEY (persisted to secret-key, mode 0600) under ~/.config/kemeter/ring/ (or $RING_CONFIG_DIR).
ring init
Interactive on a TTY (prompts for runtime and API port); scriptable via flags otherwise:
--runtime:docker,podman,cloud-hypervisor,firecracker(experimental), orboth(Docker + Cloud Hypervisor).bothnever enables Firecracker.--port: the API port (default3030).--force: overwrite an existingconfig.toml/secret-key. Regenerates the key, making every secret stored under the old one undecryptable.
Flags win over prompts and over the non-interactive defaults (Docker, port 3030), and compose per-field. When stdin is not a TTY and no flags are given, init uses the defaults instead of hanging on a prompt.
After writing the files, init runs the same diagnostics as ring doctor on the selected runtime as a pre-flight check. Failing checks are surfaced as warnings but do not change the exit code, since init already succeeded. Only the selected runtime is checked.
ring initdoes not create the SQLite database or seed the admin user. That happens automatically the first timering server startruns the migrations.
ring doctor
Run diagnostic checks against the host. Runtime checks only run for runtimes enabled in config.toml; with no runtime enabled, all of them run.
- Server-side env:
RING_SECRET_KEYis set, decodes to base64, and is exactly 32 bytes. - Docker / Podman:
docker --versionsucceeds (the binary is present and the daemon is reachable). Podman reuses this check since it speaks the Docker API. - Cloud Hypervisor: the binary is on
$PATH,/dev/kvmis readable+writable, the binary hascap_net_admin,cap_net_rawset (printed bygetcap),xorrisois on$PATH(needed for the cloud-init NoCloud ISO when a CH deployment shipsenvironment), the firmware file at the configuredfirmware_pathexists, and avirtiofsdbinary is found at/usr/libexec/virtiofsd,/usr/lib/qemu/virtiofsd, or whateverRING_VIRTIOFSDpoints to. - Firecracker (experimental, only when enabled): the binary is on
$PATH,/dev/kvmis readable+writable, the binary hascap_net_admin,cap_net_rawset, the kernel image at the configuredkernel_path(an uncompressedvmlinux, not a firmware blob) exists, andsocatis on$PATHfor port forwarding.
ring doctor
Use this as the first step when something doesn't work as expected. Exits non-zero if any check fails.
Server
ring server start
Start the Ring server.
ring server start
On first start the server runs SQLite migrations, creates ring.db in the working directory (override with RING_DATABASE_PATH), and seeds the default admin / changeme user. Set RUST_LOG=info to see logs.
Authentication
ring login
Log in to a Ring server. Each login mints a fresh session token (a ring_pat_… value, full access) saved in ~/.config/kemeter/ring/auth.json and reused by subsequent commands. The session does not expire; end it with ring logout. Logging in again creates an independent session, and earlier ones stay valid until they are logged out.
ring login --username <USERNAME> --password <PASSWORD>
Required:
--username <USERNAME>/-u--password <PASSWORD>/-p
Examples:
ring login --username admin --password changeme ring login -u alice -p secret
If the server is unreachable (down, wrong host/port), the CLI prints a single actionable line and exits non-zero, not the underlying transport trace:
$ ring login -u admin -p changeme error: cannot reach the server at http://localhost:3030 — is it running? (connection refused)
A timeout (the server ... did not respond in time) and a malformed
request are reported the same way: one line, no nested error chain.
ring logout
Revoke the current context's session on the server and remove its token from auth.json.
ring logout
The server-side revocation is best-effort: if the server is unreachable the local token is still removed, so the command never leaves you "stuck logged in" locally. Only the current context's entry is touched; other contexts in auth.json are left intact. Revoking a PAT (rather than a login session) is done with ring token revoke <id>, not this command.
Deployments
ring apply
Apply a deployment manifest.
ring apply -f <FILE> [OPTIONS]
Options:
-f <FILE>/--file <FILE>: YAML or JSON manifest-e <FILE>/--env-file <FILE>: loadKEY=VALUEpairs from a file and use them to interpolate$VARreferences in the manifest-d/--dry-run: print what would be sent, without contacting the API--verbose: print the full JSON of every deployment that will be sent--force: skip the rolling-update path; do an immediate replacement even when health checks are configured
Examples:
ring apply -f deployment.yaml ring apply -f config.json ring apply -f app.yaml --env-file .env ring apply -f app.yaml --dry-run --verbose ring apply -f app.yaml --force
The manifest can contain a top-level namespaces: map and a deployments: map. See the file format section below.
ring deployment list
List deployments. Defaults to all namespaces.
ring deployment list [OPTIONS]
Options:
-n/--namespace <NAMESPACE>: filter by namespace-s/--status <STATUS>: filter by status (repeatable). Values:pending,creating,running,completed,failed,deleted,crash_loop_back_off,image_pull_back_off,create_container_error,network_error,config_error,file_system_error,insufficient_resources,error(see Deployment status lifecycle)--type <TYPE>: filter by deployment kind:workerorjob-l/--label <SELECTOR>: filter by label,key=valueor justkey(repeatable; a deployment must match all selectors). Works the same across runtimes (Docker and Cloud Hypervisor) since labels are matched on Ring's stored metadata.-o/--output <FORMAT>:table(default) orjson
Output (table):
The table has ten columns: Id, Created at (UTC), Updated at (UTC), Namespace, Name, Image, Runtime, Kind, Replicas (formatted instances/desired), Status.
Timestamps are rendered to the second (2026-05-03 22:22:21); sub-second
digits and the UTC suffix are dropped from the cells since every Ring
timestamp is UTC, as the column header says. The json output keeps the
raw timestamp untouched, so scripts that parse it are unaffected.
Examples:
ring deployment list ring deployment list --namespace production ring deployment list --status running ring deployment list --status running --status pending ring deployment list --type job ring deployment list --label env=prod ring deployment list -l env=prod -l tier=web # both must match ring deployment list -o json | jq -r '.[].id'
ring deployment inspect
Show the full state of a deployment.
ring deployment inspect <DEPLOYMENT_ID>
<DEPLOYMENT_ID> is the UUID printed by ring deployment list.
ring deployment delete
Delete a deployment. The deployment is marked deleted and the scheduler removes its containers on the next tick.
ring deployment delete <DEPLOYMENT_ID>
ring deployment logs
Tail the logs of a deployment's containers.
ring deployment logs <DEPLOYMENT_ID> [OPTIONS]
Options:
-f/--follow: stream new lines (polls every 2 s)--tail <N>: last N lines (default: 100)--since <DURATION>: relative duration (30s,10m,2h) or RFC3339 timestamp-c/--container <NAME>: filter to one instance/container name
Examples:
ring deployment logs web-app ring deployment logs web-app --follow ring deployment logs web-app --tail 50 ring deployment logs web-app --since 10m ring deployment logs web-app --container production_web-app # name prefix, or full container ID prefix
ring deployment events
Show scheduler events for a deployment.
ring deployment events <DEPLOYMENT_ID> [OPTIONS]
Options:
-f/--follow: stream new events-l/--level <LEVEL>: filter byinfo,warning, orerror--limit <N>: maximum number of events (default: 50)
Examples:
ring deployment events web-app ring deployment events web-app --follow ring deployment events web-app --level error ring deployment events web-app --limit 10
ring deployment metrics
Show CPU / memory / network / disk / pid stats for each instance of a deployment.
ring deployment metrics <DEPLOYMENT_ID>
Metrics are only available for the Docker runtime. Cloud Hypervisor deployments return an empty list.
ring deployment health-checks
Show the most recent health-check results for a deployment.
ring deployment health-checks <DEPLOYMENT_ID> [OPTIONS]
Options:
--latest: only the most recent result per instance--limit <N>: maximum number of results
Users
ring user list
ring user list
ring user create
ring user create --username <USERNAME> --password <PASSWORD>
Required:
--username <USERNAME>--password <PASSWORD>
ring user update
Update the currently authenticated user (the one whose token is in auth.json). At least one of --username or --password must be provided. There is no CLI command to update another user; for that, call PUT /users/{id} against the API directly.
ring user update [--username <USERNAME>] [--password <NEW_PASSWORD>]
Examples:
ring user update --password newsecret ring user update --username alice ring user update --username alice --password newsecret
ring user delete
ring user delete <ID>
<ID> is the user's UUID. Find it with ring user list.
API Tokens
Scoped API tokens (Personal Access Tokens) let scripts, CI and external agents call the API without using a human's login session. A token carries a set of verb:resource scopes, an optional namespace boundary and an optional expiry, and can be revoked or rotated individually.
The clear token (ring_pat_…) is shown once, at creation, and never again; only its prefix is stored in clear for display. Present it as Authorization: Bearer ring_pat_….
Login sessions (ring login) use the same storage and format: a session is a token scoped admin, created automatically on login and revoked on ring logout. It is distinguished from a PAT by its kind (not by its name), so naming a PAT session is fine and has no special effect. Sessions are not shown by ring token list and cannot be managed by id (ring token revoke/rotate); that command lists and acts only on the PATs you created. End a session with ring logout.
Scopes: deployments:read, deployments:write, secrets:read, secrets:write, configs:read, configs:write, namespaces:read, namespaces:write, users:read, users:write, and admin (grants everything).
ring token create
ring token create <NAME> --scope <SCOPE> [--scope <SCOPE>...] [OPTIONS]
Required:
<NAME>: token label (positional)-s/--scope <SCOPE>: at least one; repeatable
Options:
-n/--namespace <NS>: restrict to a namespace; repeatable. Omit for all namespaces.-e/--expires <DURATION>:30d,12hor90m. Omit for no expiry.
The clear token is printed on stdout (so it can be captured); the summary and the "won't be shown again" notice go to stderr.
# Read-only CI token, all namespaces, 90-day expiry TOKEN=$(ring token create ci-read --scope deployments:read --expires 90d) # Write token scoped to a single namespace ring token create deployer --scope deployments:write --namespace production
ring token list
ring token list
Lists your tokens with prefix, scopes, namespaces, status (active / revoked / expired), last-used and expiry. The secret is never shown.
ring token revoke
ring token revoke <ID> [-y|--yes]
Revokes a token immediately. Prompts for confirmation on a terminal; in a pipe/CI it refuses unless --yes is passed.
ring token rotate
ring token rotate <ID>
Revokes the token and mints a new one with the same name, scopes, namespaces and expiry. The new clear value is printed on stdout (shown once).
Webhooks
Webhooks let an external endpoint receive Ring events by HTTP POST instead of polling the API. Each webhook subscribes to a set of event kinds (or all of them) and is delivered through a durable queue with retry and dead-lettering, so a transient outage on the receiver doesn't drop events.
Today the only event kind is deployment.status_changed, emitted on every deployment status transition (creating→running, →failed, →completed, scaling, …). The payload carries old_status, new_status, the deployment id/name/namespace/kind and restart_count. When the webhook has a secret, deliveries are signed with X-Ring-Signature: sha256=<hmac>.
Managing webhooks requires the webhooks:write scope (or webhooks:read to list).
ring webhook create
ring webhook create <URL> [--event <KIND>...] [--secret <SECRET>]
Required:
<URL>: target URL receiving the signed POSTs (positional)
Options:
-e/--event <KIND>: subscribe to an event kind; repeatable. Accepts an exact kind (deployment.scaled), a family wildcard (deployment.*), or*for everything. Omit to receive all kinds. A malformed value (e.g.deployment*without the dot) is rejected at creation.-s/--secret <SECRET>: HMAC secret. If omitted, Ring generates one and prints it once.
The webhook id is printed on stdout; the generated secret (if any) is printed on stderr and shown only once.
# Exact kinds ring webhook create https://hooks.example.com/ring --event deployment.status_changed # Every deployment event ring webhook create https://hooks.example.com/ring --event 'deployment.*'
ring webhook list
ring webhook list
Lists webhooks with their URL, subscribed events, status (active/revoked) and creation time. Secrets are never shown.
ring webhook delete
ring webhook delete <ID>
<ID> comes from ring webhook list.
Secrets
Secrets are AES-256-GCM-encrypted values stored per-namespace. RING_SECRET_KEY (a base64-encoded 32-byte key) must be exported before ring server start, or the server refuses to start. Run ring doctor to confirm the variable is set and decodes correctly.
ring secret create
ring secret create <NAME> -n <NAMESPACE> -v <VALUE>
Required:
<NAME>: secret name (positional)-n/--namespace <NAMESPACE>-v/--value <VALUE>
Examples:
ring secret create database-password -n production -v "s3cret!" ring secret create api-key -n staging -v "sk-1234567890"
ring secret list
Lists secret metadata. Values are never returned through the API or the CLI.
ring secret list [OPTIONS]
Options:
-n/--namespace <NAMESPACE>: filter by namespace
ring secret delete
ring secret delete <ID> [OPTIONS]
Options:
-f/--force: delete even if referenced by active deployments
If the secret is referenced and --force is not set, Ring lists the referencing deployments and aborts.
Configs
A config is a named blob (typically a config file or a JSON document) that can be mounted into a deployment via a volume of type: config.
The CLI exposes list, inspect, and delete. Creation goes through the REST API (POST /configs).
ring config list
ring config list [OPTIONS]
Options:
-n/--namespace <NAMESPACE>
ring config inspect
ring config inspect <CONFIG_ID>
ring config delete
ring config delete <CONFIG_ID>
Namespaces
ring namespace create
ring namespace create <NAME>
For Docker-runtime deployments, each namespace gets a dedicated Docker bridge network (ring_<name>). Namespaces are also auto-created when a deployment is applied to a non-existent namespace. The Cloud Hypervisor runtime does not create per-namespace networks; see Cloud Hypervisor for the VM networking model.
ring namespace list
ring namespace list
ring namespace prune
Remove inactive deployments from a namespace.
ring namespace prune <NAMESPACE> [--all]
Options:
-a/--all: delete every deployment in the namespace, including running ones. Destructive.
Prunable statuses (default): completed, failed, deleted, crash_loop_back_off, image_pull_back_off, create_container_error, network_error, config_error, file_system_error, error.
Preserved statuses (default): pending, creating, running.
Examples:
ring namespace prune development ring namespace prune development --all
Node
ring node get
Display node information.
ring node get
Returns: hostname, os, arch, uptime, cpu_count, memory_total, memory_available, load_average.
Contexts
A context is a named connection profile in config.toml.
ring context
ring context [SUBCOMMAND]
Subcommands:
configs(default): list all contextscurrent-context: print the currently active context nameuser-token: print the authentication token for the current context
Examples:
ring context ring context configs ring context current-context ring context user-token
Configuration files
Contexts and tokens live in ~/.config/kemeter/ring/ (or $RING_CONFIG_DIR):
config.toml: context definitionsauth.json: authentication tokens per context
config.toml example:
[contexts.default] current = true host = "127.0.0.1" api.scheme = "http" api.port = 3030 [contexts.production] current = false host = "prod.example.com" api.scheme = "https" api.port = 443 [scheduler] interval = 10
Using contexts
ring --context production deployment list ring -c staging server start
The default context (the one with current = true) is used when no --context flag is provided.
Environment variables
Server
RING_DATABASE_PATH: path to the SQLite file (default:./ring.db)RING_DB_POOL_SIZE: max SQLite connections (default:5)RING_CONFIG_DIR: config directory (default:~/.config/kemeter/ring)RING_SECRET_KEY: base64-encoded 32-byte key for secret encryption. Required: the server refuses to start without it (validated up front; seering doctor).RING_SCHEDULER_INTERVAL: scheduler tick in seconds (overridesserver.scheduler.intervalinconfig.toml)RING_APPLY_TIMEOUT: single-deployment apply timeout in seconds (default:300)RUST_LOG: log level (e.g.info,debug,ring=debug)
CLI
RING_TOKEN: bearer token used for API requests. When set and non-empty, the CLI ignoresauth.json. Useful for CI pipelines that should not depend onring login.RING_CONFIG_FILE: path to a specificconfig.tomlto load (default:$RING_CONFIG_DIR/config.toml). The--configflag takes precedence.
# Generate a server-side key export RING_SECRET_KEY="$(openssl rand -base64 32)" ring server start
Exit codes
| Code | Name | Triggered when |
|---|---|---|
0 | Success | The command completed successfully (HTTP 2xx) |
1 | General error | Validation, parsing, or any non-categorized failure |
2 | Auth | API responded with 401 Unauthorized or 403 Forbidden |
3 | Connection | CLI could not reach the API (network, DNS, timeout, refused) |
4 | Not found | API responded with 404 Not Found |
5 | Conflict | API responded with 409 Conflict (e.g. resource already exists) |
Conditional create in a shell script:
ring deployment inspect "$DEPLOYMENT_ID" > /dev/null 2>&1 case $? in 0) echo "already deployed, skipping" ;; 4) ring apply -f deployment.yaml ;; 2) echo "auth expired"; exit 1 ;; 3) echo "API unreachable"; exit 1 ;; *) echo "unexpected error"; exit 1 ;; esac
Notes:
- Every command that talks to the API honours this table: a command that cannot reach the API or gets a non-2xx response exits non-zero (never
0), soset -e, CI gates and&&chains detect the failure. ring applyprocesses multiple deployments; if any fail, the command exits with the code of the first failure.- Follow modes (
logs --follow,events --follow) keep running on transient errors and only exit if the initial request fails.
File formats
Manifest structure
Required deployment fields:
nameruntime:dockerorcloud-hypervisorimagenamespace
Optional fields:
kind:worker(default) orjobreplicas: default1; jobs always run a single instanceenvironment: map of plain values or{ secretRef: <name> }referencesvolumes: list of volume objects (see below)labels: key/value map (or list of single-key objects)command: list of arguments overriding the image entrypointresources:limits/requestsfor CPU and memoryhealth_checks: list oftcp,http, orcommandchecksconfig: image pull policy, registry auth, optionaluser
YAML example
namespaces: production: name: production deployments: app-name: name: app-name namespace: production runtime: docker kind: worker # "worker" (default) or "job" image: "nginx:1.25" replicas: 3 environment: ENV_VAR: "value" DB_PASSWORD: secretRef: "database-password" volumes: - type: bind source: /var/lib/app destination: /data driver: local permission: rw labels: app: app-name tier: backend command: - "/bin/sh" - "-c" - "exec myapp --port $PORT" resources: limits: cpu: "500m" # 500 millicores = 0.5 CPU memory: "512Mi" requests: cpu: "100m" memory: "128Mi" health_checks: - type: http url: "http://localhost:8080/health" interval: "30s" timeout: "5s" threshold: 3 # default: 3 on_failure: restart # restart | stop | alert - type: tcp port: 5432 interval: "10s" timeout: "2s" on_failure: alert - type: command command: "pg_isready -U postgres" interval: "15s" timeout: "3s" on_failure: restart
Volumes
Three type values are supported:
bind: host path mountvolume: named Docker volumeconfig: file rendered from a Ring config and mounted atdestination. Requires akeyfield that selects which entry in the config to mount;permissionis forced toro.
Required fields: type, source, destination. key is required for type: config. driver and permission have defaults via ring apply (local and rw respectively, except config which is ro); they are required at the API level (see manifest reference → volumes).
volumes: - type: bind source: /etc/nginx/conf.d/custom.conf destination: /etc/nginx/conf.d/custom.conf driver: local permission: ro - type: volume source: app-data destination: /data driver: local permission: rw - type: config source: nginx-config # name of a Ring config in the same namespace key: site.conf # which entry inside the config destination: /etc/nginx/conf.d/site.conf driver: local permission: ro
Resources
limits: hard cap on Docker (CPU throttled vianano_cpus, OOM-killed on memory overage). Allocation, not a cap, on Cloud Hypervisor (vCPU count + RAM size).requests: onlyrequests.memoryis honored on Docker (mapped tomemory_reservation, a soft minimum).requests.cpuis currently ignored on both runtimes.- CPU values: millicores (
"500m") or whole cores ("1","0.5"). On Cloud Hypervisor, fractional values are rounded down to whole vCPU (floor of 1). - Memory values: raw bytes or
Ki/Mi/Gisuffixes. - Both
limitsandrequestsare optional; within each,cpuandmemoryare also optional.
Health checks
type: tcp: checks a TCP port is open. Requiresport. Probe runs from the host against the runtime-private IP (Docker bridge IP / CH guest IP).type: http: issues an HTTP GET, expects a 2xx response. Requiresurl.localhostin the URL is rewritten to the runtime-private IP.type: command: runs a shell command inside the container viadocker exec. Requirescommand. Currently the probe only checks that exec started without error; the command's exit code is not inspected (so a script that exits non-zero will still report success). Docker only.intervalandtimeoutuse duration suffixesmsands.mandhare not supported in this context (write60s, not1m). The--sinceflag on logs is a separate parser that does acceptm/h.intervalis currently advisory: the actual cadence is one probe per scheduler tick (default 10s).threshold: consecutive failures beforeon_failuretriggers (default: 3).on_failure:restart(recreate the instance),stop(mark the deploymentdeleted), oralert(emit anerrorevent only).- Cloud Hypervisor:
tcpandhttpare supported;commandis rejected at the API.
Namespaces in YAML
The top-level namespaces: section is optional. When present, namespaces are created before deployments are processed. If a namespace already exists, it is silently skipped. Namespaces are also auto-created on first deployment.
JSON
{ "name": "app-name", "runtime": "docker", "namespace": "default", "kind": "worker", "replicas": 1, "image": "nginx:1.25", "labels": {}, "environment": { "ENV_VAR": "value", "DB_PASSWORD": { "secretRef": "database-password" } }, "volumes": [ { "type": "bind", "source": "/var/lib/app", "destination": "/data", "driver": "local", "permission": "rw" } ] }
Patterns
Variable interpolation
ring apply interpolates $VAR references in string fields (image, namespace, name, environment values, command arguments) from your shell environment, or from a file passed with --env-file.
export APP_VERSION=v1.2.3 export NAMESPACE=production ring apply -f template.yaml
deployments: app: name: myapp image: "myapp:$APP_VERSION" namespace: "$NAMESPACE" replicas: 3
CI deployment script
#!/bin/bash set -euo pipefail # Use a token-based auth flow rather than `ring login` export RING_TOKEN="$RING_API_TOKEN" ring apply -f production.yaml ring deployment list --namespace production
Troubleshooting
Diagnostics
ring doctor curl http://localhost:3030/healthz RUST_LOG=debug ring server start docker ps --filter "label=ring_deployment" docker network ls | grep '^.\+ring_'
Reset
# List all deployment IDs as JSON, then delete each one ring deployment list -o json | jq -r '.[].id' | xargs -I {} ring deployment delete {} # Force-stop everything Ring-labelled at the Docker level docker ps -a --filter "label=ring_deployment" -q | xargs -r docker rm -f # Wipe the database (server must be stopped first) rm -f ring.db ring.db-shm ring.db-wal
For a single-command help on any subcommand:
ring <command> --help
Error messages
When a command fails, the CLI prints a single human line to stderr,
prefixed with error:, and exits non-zero. It does not echo the raw
HTTP status: the status code is a category, and the message is composed
from what the command already knows (the resource and the name you typed).
$ ring deployment delete web error: deployment 'web' not found $ ring deployment inspect web error: deployment 'web' not found $ ring config delete app error: config 'app' not found
Mapping by status category:
| Situation | Message |
|---|---|
Resource missing (404) | error: <kind> '<name>' not found |
Conflict / has dependents (409) | error: <kind> '<name>' already exists or still has dependents |
Not authorized (401/403) | error: not authorized to act on <kind> '<name>' |
| Listing, not authorized | error: cannot list <kind> in namespace '<ns>': not authorized |
| Anything else | error: <kind> '<name>': request failed (<status>) |
Validation failures on apply / user create are the exception: the
server returns a structured RFC 7807 problem+json body, and the CLI
renders every violation with its property path (see ring apply). The
exit code still reflects the HTTP status in all cases.
Colour
Output is colourised only when stdout is a real terminal and the
NO_COLOR environment variable is unset:
- Errors (
error: …) are red, success lines green. - In
ring deployment list, theStatuscolumn is colour-coded: green =running/completed, yellow =pending/starting/deleted, red = the failure states (failed,crash_loop_back_off,create_container_error, …). Unknown values are left uncoloured.
When the output is piped, redirected to a file, run under CI, or
NO_COLOR=1 is set, no ANSI escape is emitted at all, and the bytes are
identical to a plain run. --output json is never colourised. This means
ring deployment list | grep, ... -o json | jq, and existing scripts
keep working unchanged. There is no --color flag; the automatic
detection is the whole contract.