Ignition
ReferenceResources

exec

Run arbitrary commands on remote hosts.

The exec resource runs commands on the remote host via SSH. It is imperative by default, with optional guard conditions (unless / onlyIf) for conditional execution.

Input

FieldTypeDefaultRequiredDescription
commandstringYesThe command to run
sudobooleanfalseNoRun with sudo sh -c
cwdstringNoWorking directory
envRecord<string, string>NoEnvironment variables
checkbooleantrueNoIf false, non-zero exit codes are not failures
unlessstringNoSkip apply when this guard command exits 0
onlyIfstringNoRun apply only when this guard command exits 0

Output

FieldTypeDescription
exitCodenumberExit code of the command
stdoutstringStandard output
stderrstringStandard error

Behavior

Check phase

Without guards, check() returns inDesiredState: false (imperative behavior).

With guards:

  • unless: if guard exits 0, resource is treated as already in desired state (apply is skipped)
  • onlyIf: if guard exits non-zero, precondition is not met and apply is skipped
  • unless and onlyIf are mutually exclusive
  • Guard commands are executed during check() itself, including ignition run --check, so only use read-only guards if you need dry-run safety

Apply phase

  1. Builds the command string:
    • Prepends environment variables as KEY=VALUE pairs
    • Wraps with cd <cwd> && if cwd is specified
    • Wraps with sudo sh -c '...' if sudo is true
  2. Executes the command via SSH.
  3. If the command exits with non-zero and check is true, throws SSHCommandError.
  4. If check is false, the exit code is captured but not treated as a failure.

Annotations

PropertyValue
NatureImperative
IdempotentNo
DestructiveYes
Read-onlyNo
Required capabilitiesexec

Examples

Basic command

await exec({ command: "nginx -t" })

With sudo

await exec({ command: "systemctl daemon-reload", sudo: true })

With working directory

await exec({ command: "npm install --production", cwd: "/opt/app" })

With environment variables

await exec({
  command: "npm run build",
  cwd: "/opt/app",
  env: { NODE_ENV: "production", CI: "true" },
})

Tolerating non-zero exit codes

await exec({ command: "grep -q 'done' /tmp/flag", check: false })

With check: false, the resource won't fail even if the command returns a non-zero exit code. The exit code is still captured in the output.

Guarded execution

await exec({
  command: "npm install -g pm2",
  unless: "command -v pm2",
  sudo: true,
})

Capturing output

const result = await exec({ command: "cat /etc/hostname" })
const hostname = result.output?.stdout.trim()

Gotchas

  • Unguarded exec always reports changed because it can't determine whether the command mutated state.
  • In check mode (ignition run --check), unguarded exec reports as "would change" since it's imperative.
  • unless can report ok when the guard shows the desired state is already met.
  • onlyIf can also report ok, but that means the precondition failed and apply was skipped.
  • unless and onlyIf guards run during check() and in ignition run --check.
  • Set check: false for commands where a non-zero exit code is acceptable (e.g., grep returning 1 for no match).
  • sudo wraps the entire command with sudo sh -c '...' — shell features like pipes and redirects work inside.
  • Command output (stdout/stderr) can be streamed in real time when trace mode is enabled.

On this page