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 viamarea 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 / option | Result |
|---|---|
--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 owner | Remote node |
|---|---|
| Deployment | <deployment>@<pod-ip> |
| StatefulSet | <pod-name>@<pod-ip> |
Cookie resolution order:
--cookie <c>on the command line.deploys.<deploy>.cookiefrommarea.yaml(resolved viasecret!file!keyif 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-cookieHow 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
lib/marea/plugins/k8s.expriv/templates/k8s/remote.exs— the helper script written to<state_dir>/remote.exsbymarea k8s remote.