Self-hosted service
nats-io/natscli avatar
nats-io/natscli

nats CLI: JetStream, key-value and messaging in one Go binary

The NATS Command Line Interface

832 stars176 forksGoApache-2.0

At a glance

What is it?
natscli is the command line interface for NATS, covering JetStream, key-value, object store, service APIs, backup, messaging and benchmarking. It ships as a single Go binary with named configuration contexts, and it is still on a 0.x version line.
Who is it for?
Operators and developers who work with NATS day to day should adopt the nats CLI, because it replaces a folder of shell scripts and hand-written API calls with one binary that understands JetStream, key-value, object store and configuration contexts.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One binary covering JetStream, KV, object store and plain messaging

natscli is the command line utility for interacting with and managing NATS. The feature list in the README is long enough to be worth reading as a map of the platform rather than of a single tool: JetStream management, key-value management, object store management, service API management, JetStream data and configuration backup, message publish and subscribe, service requests and creation, benchmarking and latency testing, deep system account inspection and reporting, configuration context maintenance, the NATS eco-system schema registry, and monitoring of streams, consumers, services, servers and credentials.

That breadth is the case for using it. A NATS deployment has surfaces that would otherwise need separate tooling: messaging itself, the persistent JetStream layer, the key-value and object store APIs layered on top of it, and the service abstraction. The CLI gives one command vocabulary across them.

The implementation is a single Go module. The go.mod file declares the module as github.com/nats-io/natscli and requires Go 1.26.0, with the nats command living in the nats directory and supporting packages in cli, internal, options, top, columns and plugins. The distribution shape is what you would expect from a Go CLI: one static binary per platform, with releases published to GitHub as Zip, RPM and DEB packages.

The scope boundary is worth stating plainly. This is a client, not the server. It manages and inspects a NATS system that runs elsewhere, and it assumes the nats-server and the JetStream API are reachable from where you run it.

Configuration contexts: named profiles stored as JSON

The CLI supports multiple named configurations, and this is the first thing you set up because every later command inherits the selected context. Contexts live in ~/.config/nats/context and are stored as JSON documents. The README points you at nats --help and its global flags for the authoritative description of what those documents contain.

Adding a context captures a server and a description. To capture the default local server:

bash
nats context add localhost --description "Localhost"

and to add a remote one and make it the default in the same step:

bash
nats context add nats --server demo.nats.io:4222 --description "NATS Demo" --select

The list command shows the contexts with a star marking the default:

bash
nats context ls

Switching is explicit, either by name or by stepping back to the previous one:

bash
nats ctx select localhost

The README notes that authentication configuration can be added at context creation time, such as user and password or creds, either as command line arguments or by editing the whole context with nats context edit localhost.

That last point is the one to think about. A credential typed on the command line ends up in your shell history, and a context file holding creds is a JSON file on disk with no protection described in the README. For a workstation this is fine, and for a shared or production host it deserves a separate look at who can read that file.

Publishing with templates, counts and HTTP style headers

Publishing is the simplest thing to try, and the CLI is at its most scriptable here. The subscriber side is one command:

bash
nats sub cli.demo

and the publisher publishes a literal payload:

bash
nats pub cli.demo "hello world"

Messages can be generated in bulk with a template that is evaluated per message. The count and timestamp are available as template variables, so a loop of five messages is a single command:

bash
nats pub cli.demo "message {{.Count}} @ {{.TimeStamp}}" --count=5

The same template syntax shows up later in the reply command, where {{2}} refers to the second token of the request subject. That consistency is worth remembering, because it means the subject layout can drive what your service does.

Reading from standard input works too, which is how you pipe a file or another command into NATS:

bash
echo hello|nats pub cli.demo

And since NATS supports HTTP style headers, the CLI accepts them with the same -H flag curl uses:

bash
nats pub cli.demo 'hello headers' -H Header1:One -H Header2:Two

On the subscriber side the reply subject is included in the output, so you can see the request pattern without extra tooling. Queue groups are supported as well, with nats sub cli.demo --queue=Q1, which load shares messages between the members of the group.

Service requests and replies, with the handler as a shell command

The CLI can act as a NATS service, which is where it stops being a message tool and starts being a scripting tool. A reply command subscribes to a subject pattern and answers anything that arrives:

bash
nats reply 'cli.weather.>' "Weather Service"

The reply subject is picked up automatically, and the group name in the output, NATS-RPLY-22, is the queue group the service joins so that load is shared if you run more than one.

The command argument is not limited to a static string. Using the same template variables as the publish command, you can hand the request to an external program:

bash
nats reply 'cli.weather.>' --command "curl -s wttr.in/{{2}}?format=3"

Here {{2}} is the second token of the request subject, which for a request on cli.weather.london is the city name. The service therefore becomes a thin adapter that routes a subject to a command.

On the other side, the request command sends a request and waits for the response, printing the round trip time:

bash
nats request "cli.weather.london" ''

The subject can also carry wildcards in a token position, which is how one request reaches several cities at once:

bash
nats request "cli.weather.{london,newyork}" '' --raw

JetStream push consumers are covered too. Passing --ack to a subscription acknowledges messages as they arrive, and the CLI prints the consumer, the stream and subject, delivery counts and stream sequence numbers alongside the payload.

Four install paths and a nightly image

Because the project publishes releases to GitHub as Zip, RPM and DEB packages for various operating systems, the packaged route is always available. The Go toolchain route installs the latest version directly from the module path:

bash
go install github.com/nats-io/natscli/nats@latest

and pinning a specific release uses the same command with a version suffix:

bash
go install github.com/nats-io/natscli/[email protected]

That pinned form is the one to use in CI, since it makes the build reproducible.

For macOS, Homebrew installs the latest released version:

bash
brew install nats

Windows users have scoop, which needs the extras bucket added first:

bash
scoop bucket add extras
scoop install extras/natscli

Arch Linux has an AUR package:

bash
yay natscli

And for the case where you want to run against a server that is itself moving, nightly builds are included in the synadia/nats-server:nightly Docker images, so the CLI ships inside the same nightly image as the server it talks to.

Where the nats CLI is the wrong tool

The CLI is a client for an existing NATS system, and several things follow from that.

It is not a server. There is no broker, no persistence layer and no cluster management here; the features named in the README, such as deep system account inspection, act on a running server you point at. If you are trying to stand up messaging rather than operate it, this is the wrong binary.

It is not a library either. The repository depends on github.com/nats-io/jsm.go for JetStream management and github.com/nats-io/nats.go for the core client, which is the real difference between the two approaches. The CLI is a wrapper around those libraries that adds contexts, schema registry access, backup and benchmarking. If your automation runs on a schedule with retries and structured logs, driving nats.go from a small Go program gives you error handling and testability that shell does not, and you give up the CLI commands that have no library equivalent.

The documentation is also uneven. For JetStream management the README simply refers you to the JetStream Documentation and notes that JetStream became GA as of nats-server v2.2.0. The README is a starting point for the messaging commands, not a complete reference for every feature it lists.

Finally, this is an operator tool with an operator's failure mode. Commands act on a live system, several of them write or delete configuration, and the credential story described earlier applies to any account you automate against.

Apache-2.0 licensing and a 0.x version line to watch

The licence is Apache-2.0, which is permissive and includes an explicit patent grant, so embedding the CLI or redistributing the binaries in an internal tooling bundle is a normal use. The LICENSE file at the repository root is the text that governs, and its notice requirements still apply to redistributed copies.

The version line is the thing to keep an eye on. The recent releases are v0.3.2 on 2026-03-25, v0.4.0 on 2026-05-01, and v0.5.0 on 2026-09-17. A project at 0.x is telling you that flags and command shapes can still change between minor versions, and the README itself demonstrates a specific release install with go install github.com/nats-io/natscli/[email protected], which is a much older tag than any of those. That difference between the version in the README example and the current release line is a fair signal that command syntax has moved over time.

The repository is not archived and the last push was on 2026-10-16, so work is still landing between releases. The Go toolchain requirement of 1.26.0 in go.mod is also worth noting if you build the CLI yourself, since that sets the floor for your own build environment.

The project also carries the usual governance files for an open source project, including CONTRIBUTING.md, CODE_OF_CONDUCT.md, GOVERNANCE.md, MAINTAINERS.md and AUTH.md, which tells you where decisions get made when you want to report something or propose a change.

Editorial conclusion

Operators and developers who work with NATS day to day should adopt the nats CLI, because it replaces a folder of shell scripts and hand-written API calls with one binary that understands JetStream, key-value, object store and configuration contexts. It is the wrong tool for long-lived automation that needs to be scheduled and monitored, since the context file stores credentials as plain JSON under ~/.config/nats/context, and it is the wrong tool for anyone who wants stability, since v0.5.0 is still a pre-1.0 release whose flags can change between minor versions. Before adopting, check the version your automation pins and read nats --help for the global flags, which the README points to as the authoritative description of the context file.

Frequently asked questions

How do I install the nats CLI?

You can run go install github.com/nats-io/natscli/nats@latest, install the latest release with brew install nats on macOS, scoop install extras/natscli on Windows, or yay natscli on Arch Linux. Releases are also published to GitHub as Zip, RPM and DEB packages.

Where does the nats CLI store its configuration contexts?

Contexts are stored in ~/.config/nats/context as JSON documents. You create them with nats context add, list them with nats context ls and switch between them with nats ctx select, and the README points to nats --help for the global flags that describe the file.

Can the nats CLI act as a NATS service that runs a command?

Yes. The nats reply command subscribes to a subject pattern and answers requests, and with the --command flag the handler is an external program whose template variables come from the request. The README shows curl -s wttr.in/{{2}}?format=3 used that way.

Is the nats CLI still on a pre-1.0 version?

Yes. The recent releases are v0.3.2 on 2026-03-25, v0.4.0 on 2026-05-01 and v0.5.0 on 2026-09-17, and the repository is not archived with a last push on 2026-10-16. Pin an exact version in automation rather than tracking the latest.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. nats-io/natscli on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nats-io-natscli.svg)](https://hysenlabs.com/projects/nats-io-natscli)

Was this useful?