Use Shell (shell.run) to run a multi-line script in the workflow runtime or on a remote SSH host. The same environment, standard input, structured input, files, and tenant secrets are delivered on every execution target.

Execution target

Choose Rollout to use the workflow's native, Docker, or Kubernetes runtime. Choose Remote (SSH) and an SSH Host connection to run on that host. A connectivity service such as Tailscale can carry SSH to a private network; only SSH connection failures (exit 255) are retried while that transport warms up. The remote script's own non-zero exit is never replayed by the transport.

Tenant secrets are resolved at step start on both targets, override plain environment values with the same name, and are registered for log redaction. Environment names must match ^[A-Za-z_][A-Za-z0-9_]*$.

Inputs

Input Type Description
env object Plain environment variables. Declared tenant secrets override matching keys
workdir string Working directory in the runtime, or the path on the SSH host
stdin string Bytes delivered to the script's standard input
input any JSON-encoded into the file named by $ROLLOUT_INPUT_FILE
files list Files materialized below workdir before the script starts

Each files entry has a relative path and exactly one source:

JSON
[
  {"path": "config/app.json", "content": "{\"enabled\":true}"},
  {"path": "assets/logo.png", "attachment_ref": {"id": "..."}}
]

Absolute paths, traversal (..), duplicate normalized paths, missing sources, and entries with both sources are rejected. Attachment references are resolved inside the current tenant. Docker and Kubernetes stage the payload under /rollout; SSH sends it as framed bytes on the command's single channel and extracts a tar archive below the remote workdir.

For Kubernetes, the rendered script, JSON input, stdin, files, and tenant secret values share one ephemeral Secret. Their combined payload must not exceed 1 MiB; an oversized step fails before pod creation with input_too_large. The script is mounted as /rollout/step.sh, so its size is not constrained by the container argv limit.

Outputs

Shell always returns stdout, stderr, and exit_code. capture_files collects selected workspace paths into the files output on runtimes with a shared workspace; it is unavailable for remote SSH paths.

Use Declared outputs to parse or project structured values. For example, a strict JSON stdout output is:

JSON
{
  "io": {
    "outputs": [
      {"id": "json", "source": "stdout", "parse": "json", "policy": "strict"}
    ]
  }
}

A declared output named warnings with policy warn also populates the run's warning channel. Warning strings are tenant-secret-redacted before the 50-item, 1 KiB-per-item display cap.

The removed capture_json and capture_lines switches are not accepted. The stored-config migration 20260926231000_clean_stale_node_config rewrote saved workflows, branches, releases and run flows: capture_json: true became the strict JSON stdout output above, and both switches were removed. Operators can report any config key the current manifests no longer declare with:

SH
mix rollout.flows.config_hygiene
mix rollout.flows.config_hygiene --apply

The first command is a read-only report per node type and key. --apply performs the same idempotent, tenant-atomic rewrite the migration runs.