Cluster operations

Day-two operations on a registered data-plane cluster: inspecting health, rotating the agent's join token, and tearing down infrastructure. For first-time provisioning, see Getting started: admin.

Listing and inspecting

$ adj cluster ls
$ adj cluster get prod

adj cluster get shows agent health, active instance count, and when the agent last checked in.

Status: provisioning progress + agent health

$ adj cluster status prod
ID:            1c9c14da-8b0e-4c6e-9f1a-2d7c3a5b6e42
Provisioning:  succeeded
Started:       2026-07-18T09:14:02Z
Log:           /home/dana/.config/adj/provision/1c9c14da-8b0e-4c6e-9f1a-2d7c3a5b6e42.log
Name:          prod
Agent health:  connected
Instances:     14
Last seen:     2026-07-19T08:12:41Z

Use this while adj cluster provision is running detached to watch the Provisioning: line move from running to succeeded or failed (a failure also shows the exit code and points at the log file). The Provisioning: / Started: / Log: lines come from local provision state, so they only appear on the machine that ran adj cluster provision.

Once provisioning has succeeded, the command doubles as an ongoing health check: Agent health: is connected, stale, or never (agent has never checked in), and Last seen: is the agent's last check-in time. A stale agent is the first sign of a data-plane connectivity problem (see Troubleshooting's 503/504 entries).

Kubeconfig

$ adj kubeconfig
/home/dana/.config/adj/kubeconfigs/prod.yaml

Prints the path to the kubeconfig adj cluster provision wrote for the active context — resolution order is --kubeconfig, then $ADJ_KUBECONFIG, then that generated file. Use it directly:

$ export KUBECONFIG=$(adj kubeconfig)
$ kubectl get pods -A

Your own ~/.kube/config is never touched by adj cluster provision.

Rotating the join token

$ adj cluster rotate-token prod
⚠  Agent join token (shown once — the prior token is now invalid):
x7Dq2m4T9vKcW1pZa8Rf3sLn0uYbEjH5gOiA6dNqVwM

Invalidates the cluster's previous agent join token immediately. Use this if a token may have leaked, or as routine credential hygiene. The agent running in your data plane keeps its existing connection; the new token only matters the next time an agent needs to (re)join. Note that adj cluster bootstrap and adj cluster provision also rotate the token as a side effect — if you've re-run either of those recently, rotate-token isn't the first thing that changed it.

Deprovision vs. rm — two different operations

These are easy to conflate. They do different things:

adj cluster deprovision adj cluster rm
What it does Destroys the AWS infrastructure (EKS, FSx, every clone on it) via a local OpenTofu destroy Deletes the cluster's control-plane record
Blocked by active instances? No — it destroys them along with everything else Yes — 409 while any instance is still registered against it
Requires login? Only if you pass a cluster name Always
Requires local tools? tofu + aws on this machine No

Deprovision

$ adj cluster deprovision prod
This DESTROYS the AWS data plane (EKS + FSx + all clones) for cluster prod. Proceed? [y/N]: y

Runs the OpenTofu destroy locally (including the two-pass teardown needed for stuck Kubernetes-provider resources). It does not deregister the cluster record — that's a separate step (cluster rm).

Image defaults and the version gate

Both adj cluster provision and adj cluster deprovision fetch the control plane's client config before showing their confirmation prompt:

If you pass a UUID cluster id instead of a name, deprovision needs no login — the only control-plane contact is an unauthenticated GET /v1/clientconfig call for the version gate, which proceeds (fail-open) if the control plane is unreachable — and then goes straight to the local tofu destroy using your local AWS credentials. That's deliberate: a logged-out admin, or a break-glass script, can still tear down infrastructure even if the control plane is unreachable. Passing a cluster name instead requires resolving it via the API first, which does require being logged in.

--aws-profile selects which AWS profile tofu and the AWS CLI use (default: ambient credentials / AWS_PROFILE); --yes skips the confirmation prompt.

By default deprovision (like provision) uses the OpenTofu module embedded in adj — no checkout needed. --module-dir <path> is the developer/ from-source override: point it at a local module checkout instead (needed if you originally provisioned with --module-dir; see "Where provisioning state lives" below). Deprovision requires only tofu and aws on this machine, in both embedded and --module-dir modes.

Where provisioning state lives

Embedded-mode provision/deprovision extract the module to ~/.config/adj/provision/bundles/<cluster-id>/ (honoring $XDG_CONFIG_HOME), and the OpenTofu state (terraform.tfstate, .terraform/) lives inside that same directory. Two things follow from that:

Use adj cluster bundle export <dir> to get a look at the module without provisioning anything:

$ adj cluster bundle export ./adjoint-module
exported adj v0.2.0 bundle to ./adjoint-module
module: ./adjoint-module/deploy/tofu/adjoint-cluster

It refuses to write into a non-empty directory. Two uses: read the HCL before you run it, or seed a --module-dir workflow (e.g. to carry local patches across provisions instead of relying on the embedded module).

Deregistering

$ adj cluster rm prod

Deletes the cluster's record from the control plane. A cluster with active instances cannot be removed — you'll get a 409 until every instance against it is destroyed. This is the record-keeping step; it doesn't touch AWS infrastructure at all. The normal teardown order is: destroy instances → adj cluster deprovisionadj cluster rm.

Next