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

System

ring init

Initialize the Ring config directory.

ring init

Creates ~/.config/kemeter/ring/ (or $RING_CONFIG_DIR) and writes an empty auth.json. Produces no output on success.

ring init does not create the SQLite database or seed the admin user. That happens automatically the first time ring server start runs the migrations.

ring doctor

Run diagnostic checks against the host:

  • Docker daemon connectivity
  • Cloud Hypervisor binary (for the alpha runtime)
  • KVM availability (/dev/kvm)
  • EFI firmware presence
  • virtiofsd binary presence
ring doctor

Use this as the first step when something doesn't work as expected.

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. The token is saved in ~/.config/kemeter/ring/auth.json and reused by subsequent commands.

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

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> — load KEY=VALUE pairs from a file and use them to interpolate $VAR references 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)
  • --type <TYPE> — filter by deployment kind: worker or job
  • -o / --output <FORMAT>table (default) or json

Output (table):

The table has ten columns: Id, Created at, Updated at, Namespace, Name, Image, Runtime, Kind, Replicas (formatted instances/desired), Status.

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 -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 web-app-1

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 by info, warning, or error
  • --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.

Secrets

Secrets are AES-256-GCM-encrypted values stored per-namespace. The server must be started with RING_SECRET_KEY (a base64-encoded 32-byte key) for any secret operation to succeed; without it, the API returns 500 Internal Server Error.

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>

Each namespace gets a dedicated Docker network (ring-<name>). Namespaces are also auto-created when a deployment is applied to a non-existent namespace.

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, crashloopbackoff, imagepullbackoff, createcontainererror, networkerror, configerror, filesystemerror, 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 contexts
  • current-context — print the currently active context name
  • user-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 definitions
  • auth.json — authentication tokens per context

config.toml example:

[contexts.default]
current = true
host = "127.0.0.1"
api.scheme = "http"
api.port = 3030
user.salt = "changeme"

[contexts.production]
current = false
host = "prod.example.com"
api.scheme = "https"
api.port = 443
user.salt = "changeme"

[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 to use secrets.
  • RING_SCHEDULER_INTERVAL — scheduler tick in seconds (overrides scheduler.interval in config.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 ignores auth.json. Useful for CI pipelines that should not depend on ring login.
# Generate a server-side key
export RING_SECRET_KEY="$(openssl rand -base64 32)"
ring server start

Exit codes

CodeNameTriggered when
0SuccessThe command completed successfully (HTTP 2xx)
1General errorValidation, parsing, or any non-categorized failure
2AuthAPI responded with 401 Unauthorized or 403 Forbidden
3ConnectionCLI could not reach the API (network, DNS, timeout, refused)
4Not foundAPI responded with 404 Not Found
5ConflictAPI 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:

  • ring apply processes 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:

  • name
  • runtimedocker or cloud-hypervisor
  • image
  • namespace

Optional fields:

  • kindworker (default) or job
  • replicas — default 1; jobs always run a single instance
  • environment — map of plain values or { secretRef: <name> } references
  • volumes — list of volume objects (see below)
  • labels — key/value map (or list of single-key objects)
  • command — list of arguments overriding the image entrypoint
  • resourceslimits / requests for CPU and memory
  • health_checks — list of tcp, http, or command checks
  • config — image pull policy, registry auth, optional user

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 mount
  • volume — named Docker volume
  • config — file mounted from a Ring config

Required fields: type, source, destination. Optional: driver (local or nfs, default local), permission (ro or rw, default rw).

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
    destination: /etc/nginx/conf.d/site.conf
    driver: local
    permission: ro

Resources

  • limits — hard cap (CPU throttled, OOM-killed on memory overage)
  • requests — minimum the scheduler guarantees
  • CPU values: millicores ("500m") or whole cores ("1", "0.5")
  • Memory values: raw bytes or Ki / Mi / Gi suffixes
  • Both limits and requests are optional; within each, cpu and memory are also optional

Health checks

  • type: tcp — checks a TCP port is open. Requires port.
  • type: http — issues an HTTP GET and expects a 2xx response. Requires url.
  • type: command — runs a shell command inside the container and expects exit code 0. Requires command.
  • interval and timeout use duration suffixes ms and s. m and h are not supported.
  • threshold — consecutive failures before on_failure triggers (default: 3).
  • on_failurerestart (restart the container), stop (stop it), or alert (log an event only).

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