A Kubernetes operator for NATS, modelled on the upstream nats-io/k8s Helm chart but reorganised as typed CRDs with server-side apply and no free-form escape hatches.
| Kind | Purpose |
|---|---|
NatsCluster |
A clustered NATS server: StatefulSet, Services, ConfigMap, PDB, reloader/exporter sidecars, optional websocket Ingress, JetStream, leaf nodes, MQTT, gateways, decentralized JWT auth. |
NatsBox |
Utility pod with the nats CLI pre-wired to a cluster — handy for kubectl exec debugging. |
JetStream resources (Streams, Consumers, KV, Object Store) are delegated to NACK. See NACK integration below.
helm install nats-operator \
oci://ghcr.io/crewlet/nats-operator/charts/nats-operatorThe chart bundles CRDs, RBAC, and the manager Deployment. Values are documented in the chart README.
apiVersion: nats.crewlet.cloud/v1alpha1
kind: NatsCluster
metadata:
name: my-nats
spec:
replicas: 3
image:
repository: nats
tag: 2.12.6-alpine
config:
jetstream:
enabled: true
fileStore:
pvc:
resources:
requests:
storage: 100GiConnection URLs are published in status.endpoints.
apiVersion: nats.crewlet.cloud/v1alpha1
kind: NatsBox
metadata:
name: my-box
spec:
clusterRef:
name: my-natskubectl exec -it deploy/my-box -- nats stream lsA default CLI context is generated for the referenced cluster. Add more
via spec.contexts.
Generate operator/account/user credentials with
nsc, store the JWTs
and creds in Secrets, and reference them from spec.auth.jwt:
spec:
auth:
jwt:
operator:
name: nats-operator-jwt
key: operator.jwt
systemAccount: AASYSTEM_ACCOUNT_PUBKEY
accounts:
- name: system
publicKey: AASYSTEM_ACCOUNT_PUBKEY
jwt: {name: nats-system-account-jwt, key: account.jwt}
userCreds: {name: nats-system-user-creds, key: nats.creds}
- name: app
publicKey: BBAPP_ACCOUNT_PUBKEY
jwt: {name: nats-app-account-jwt, key: account.jwt}
userCreds: {name: nats-app-user-creds, key: nats.creds}
resolver:
type: memory # or "full" for runtime account pushesJWT material is staged into an operator-managed Secret mounted at
/etc/nats-auth/ — never into a ConfigMap. Rotating a JWT Secret triggers
a hot reload via the reloader sidecar; no pod restart.
For each account with userCreds, the operator creates a NACK Account
CR named <cluster>-<account.name>, wired to the cluster endpoints and
creds Secret. NACK Streams then reference the account by name only:
apiVersion: jetstream.nats.io/v1beta2
kind: Stream
metadata:
name: orders
spec:
account: my-nats-app # auto-created from auth.jwt.accounts[]
name: ORDERS
subjects: [orders.*]If NACK isn't installed, the NackIntegrationAvailable condition goes
False and the integration lights up automatically once NACK arrives.
The NatsCluster itself is unaffected.
make generate manifests # deepcopy + CRDs
make build # manager binary
make test # unit + golden tests
make lint
make helm-sync # sync chart CRDs from config/
make helm-lint helm-template helm-packageBuilt with kubebuilder and controller-runtime.
Issues and PRs welcome. Run make lint test before submitting.