Talk to the cluster directly: describe pods, follow logs, drop into a shell, run a release-specific console script, restart a workload, or attach an iex --remsh-style remote session — all scoped to the release that's already selected via --deploy / --release.

Optional. Enable by listing Marea.Plugins.K8s under plugins: in marea.yaml. Adds no new schema fields — every command reuses deploys.<deploy>.helm.namespace, deploys.<deploy>.helm.kube_context and deploys.<deploy>.cookie from the existing deploy spec.

Non-interactive commands (describe, logs, restart) are also exposed as MCP tools via marea mcp serve. The interactive ones (shell, console, remote) use Marea's deferred-shell mechanism to replace the parent shell with a TTY- attached process; calling them via MCP captures their output but does not produce a usable interactive session. Use them from the CLI when you need a real terminal.

Commands

marea k8s
 describe   --deploy <d> --release <r> [--pos <n>]
 logs       --deploy <d> --release <r> [--pos <n>] [--tail <n>]
                                          [--since <dur>]
                                          [--previous] [--timestamps]
                                          [--prefix]
 shell      --deploy <d> --release <r> [--pos <n>]
 console    --deploy <d> --release <r> [--pos <n>]
 restart    --deploy <d> --release <r>
 remote     --deploy <d> --release <r> [--pos <n>] [--cookie <c>]

--deploy and --release default to the last values used (persisted in <state_dir>/last_values).

How a release maps to pods

For every command Marea runs:

kubectl get pods -l release=<release> -o json \
  -n <helm.namespace> [--context=<helm.kube_context>]

The pods returned by that selector are inspected and grouped by their metadata.ownerReferences:

  • Deployment (owner is a ReplicaSet): the replicaset hash is stripped to recover the deployment name; pods are sorted newest first.
  • StatefulSet: the statefulset name is taken verbatim; pods are sorted alphabetically (<name>-0, <name>-1, …).

A summary table is printed before any operation:

+--------------+-------------------------+
|  K8S LOGS                              |
+--------------+-------------------------+
| deploy       | staging                 |
| kube_context | staging-cluster         |
| namespace    | acme-staging            |
| release      | api                     |
| type         | deployment              |
| pods         | 0: api-7c8f9d4c8-abc12  |
|              | 1: api-7c8f9d4c8-def34  |
+--------------+-------------------------+

Selecting a pod with --pos

Most commands target a single pod. --pos <n> picks the n-th entry from the table above (zero-indexed, 0 is the default). With one pod the value is ignored. With several pods and no --pos, Marea prompts interactively:

pod (0-2):

logs is the exception: omitting --pos follows logs from all pods of the release at once (kubectl logs --selector release=<r> -f).

Required deploy spec

marea k8s … aborts unless the active deploy has at least:

deploys:
  staging:
    helm:
      namespace: acme-staging
      kube_context: staging-cluster   # optional but recommended
    releases:
      api: { type: elixir, helm: { template: deployment.yaml } }

kube_context is optional — if absent, kubectl uses the current context. namespace is required.

Subcommands

marea k8s describe

Resolves the active pod (--pos) and runs:

kubectl describe pod/<name> -n <ns> [--context=<ctx>]

marea k8s logs

Streams logs (-f) for the release. The base form is one of:

# no --pos: follow every pod of the release
kubectl logs --selector release=<r> -f -n <ns> [--context=<ctx>]

# with --pos: follow a single pod
kubectl logs <pod> -f -n <ns> [--context=<ctx>]

The remaining flags append modifiers:

Flag / optionResult
--tail <n>--tail <n> on kubectl
--since <dur>--since=<dur> (e.g. 5m)
--timestamps--timestamps=true
--previous--previous=true
--prefix--prefix=true

marea k8s shell

Opens an interactive shell in the selected pod:

kubectl exec -ti <pod> -n <ns> [--context=<ctx>] -- sh

marea k8s console

Same as shell, but executes /opt/console.sh instead of sh. The convention is that releases built with the default dockerfile_v01 template ship a console.sh that opens an Elixir console attached to the running release — so this is the K8s equivalent of marea run release once the release is on the cluster.

marea k8s restart

kubectl rollout restart <type> <father> -n <ns> [--context=<ctx>]

<type> is deployment or statefulset (auto-detected from the pod's owner reference); <father> is the workload name. The command is gated by Lib.pause_cmd!/3, so unless --nopause is set you'll be prompted to confirm before the restart.

marea k8s remote

Starts a hidden iex node on 127.0.0.1 and connects it to the release node running inside the chosen pod. Marea writes the helper script <state_dir>/remote.exs (embedded from priv/templates/k8s/remote.exs) and then executes:

REMOTE=<release_node>@<pod_ip> ROOT='.' iex \
  --name remote-<random>@127.0.0.1 \
  --cookie <cookie> \
  --hidden \
  --dot-iex <state_dir>/remote.exs

The remote node name follows the same rule as marea run release:

Pod ownerRemote node
Deployment<deployment>@<pod-ip>
StatefulSet<pod-name>@<pod-ip>

Cookie resolution order:

  1. --cookie <c> on the command line.
  2. deploys.<deploy>.cookie from marea.yaml (resolved via secret!file!key if it's a secret reference).

If neither is set, the command aborts with a clear error.

deploys:
  staging:
    cookie: secret!cookies!staging
# marea.d/secrets/cookies.yaml
staging: a-secret-cookie

How the deferred shell trick works

describe, logs, shell, console and remote return {:cmd, cmd} from Marea.Plugins.Base.marea_cmd/2, so the actual kubectl / iex process replaces the parent shell after the escript exits. That's how you get a working interactive session on the pod, with stdio correctly attached. See 05-architecture.md for the underlying mechanism.

restart is the only command that runs synchronously through Lib.pause_cmd!/3 (it's a fire-and-forget rollout, not an interactive session).

Source