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:
[
{"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:
{
"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:
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.