esops is a single-binary operations CLI
for self-managed Elasticsearch and
OpenSearch clusters. The name says it: “es”
for Elasticsearch (and its fork OpenSearch), “ops” for
operations — when the clusters need work, it’s time to go esops.
It’s built for SRE and DevOps work: the day-to-day maintenance
and the high-stakes migrations that pile up around any
long-lived cluster. Instead of collecting a folder of
curl | jq one-liners, you get one tool that does the job
safely.
What it is
A statically linked Go binary — no JVM, no Python runtime, no Docker. It drops straight into an Alpine or scratch image and runs anywhere.
The CLI surface is identical whether you point it at Elasticsearch or OpenSearch. The two are not pretended to be the same product — the tool branches internally where it has to (ILM vs ISM, security models, snapshot keys, reindex dialects) — but you type the same commands either way. It probes the cluster first to find out what it’s actually talking to, rather than trusting your config.
What it can do
- Cluster operations — health, node and shard overviews, allocation explain, hot threads, security audit, rebalance helper, node drain/uncordon, cluster settings, and a redacted diagnostic bundle for support tickets.
- Migrations — pre-flight checks, a mapping plan with incompatibility diffs, task-based reindex with resume and adaptive throttling, doc-count and sampled-checksum verification, and atomic alias-swap cutover. Cross-product migrations (Elasticsearch ↔︎ OpenSearch) are first-class.
- Index management — list, optimize, rollover, shrink, open/close, aliases, settings, templates, and lifecycle policies (ILM on Elasticsearch, ISM on OpenSearch).
- Snapshots — list, create, verify, restore, and retention pruning, plus repository management.
- Pipelines — list and inspect ingest pipelines.
Built to be safe in production
- Named contexts in kubectl style, each with a
protection tier. Destructive operations against a
prodcontext require an explicit--i-know-this-is-prodacknowledgement. Read-only commands and dry-runs are always allowed. - Blast-radius limits. Bulk destructive operations
refuse to run when their scope is too wide unless you opt in explicitly
— no accidental “delete everything matching
*”. - Secrets stay secret. Passwords, API keys, and
tokens are loaded by indirection (
${env:...},${file:...},${keyring:...}). Inline plaintext secrets are refused, and auth headers are redacted from logs. - Scriptable output. Table (default), JSON, NDJSON, or YAML — all with stable schemas and documented, stable exit codes. NDJSON streams large result sets without buffering. Stdout is pure data; logs and progress go to stderr.
- Pipeline-friendly. Setting
CI=trueauto-enables non-interactive mode and JSON logging, so it behaves in automation without per-script flags.
A few examples
Check cluster health, with the wide view:
$ esops ops health -o wide
NAME STATUS DIALECT VERSION NODES DATA_NODES SHARDS PRIMARIES UNASSIGNED ACTIVE%
esops-prod-eu green elasticsearch 9.3.3 6 3 120 60 0 100.0List only the indices that aren’t healthy:
$ esops index list --health yellow -o wide
NAME HEALTH STATUS PRI REP DOCS STORE CREATED
esops-stuck-replicas yellow open 1 3 1 4.2kb 2026-05-07T08:20:57ZA zero-downtime reindex, end to end:
# Estimate size/duration and flag incompatible mappings
esops migrate plan --from idx-v1 --to idx-v2
# Task-based reindex that throttles itself when the cluster gets busy
esops migrate reindex --from idx-v1 --to idx-v2 --auto-throttle
# Confirm doc counts and sampled checksums match
esops migrate verify --source idx-v1 --target idx-v2
# Atomic alias swap to the new index
esops migrate cutover --alias idx --from idx-v1 --to idx-v2Collect a redacted diagnostic bundle for a support ticket:
esops ops diag
# -> ./esops-diag-<cluster>-<timestamp>.tar.gz (credentials stripped)Configuration
Contexts work much like kubectl. Drop a config file at
~/.config/esops/config.yaml:
current-context: prod-eu
defaults:
output: table
timeout: 30s
contexts:
prod-eu:
url: https://es-prod-eu.example.com:9200
protection: prod
username: esops
password: ${keyring:esops/prod-eu}
dev:
url: http://localhost:9200
protection: noneSwitch between them on the fly:
esops config get-contexts
esops config use-context dev
esops ops health --context prod-euAuthentication covers basic auth, API keys, bearer tokens, mTLS client certificates, and AWS SigV4 (for OpenSearch behind IAM).
In short
If you operate Elasticsearch or OpenSearch and keep reaching for
curl and jq, esops replaces that
pile of incantations with one binary that’s safe to run against a live
production cluster — and speaks the same language whether you’re on
Elasticsearch or OpenSearch.