Skip to content
Dashboard
Create 5 min read

Workflows

Run an on-demand container task with run history, timeouts, and bounded retries.

Workflows#

A Workflow runs a container task on demand — not continuously like a Background Worker, and not on a fixed cron cadence like a Cron Job. Use it for bounded work that should have a recorded run history, a timeout, and retry limits instead of a permanently running process: a per-invoice render, a per-document export, a reconciliation pass, a data import that handles unexpected size by restarting rather than by staying online indefinitely.

Because each invocation is an isolated container run, retrying is explicit, bounded, and inspectable — the history records the captured exit status and application text per invocation.

When to use — and when not to#

Use a Workflow when:

  • The unit of work has a natural finish line — a document produced, a dataset exchanged, an entity migrated — and there are many distinct invocations rather than a persistent queue reader.
  • The run needs a formal timeout (kill the step after a wall-clock limit) and a retry limit (fail after N attempts) so a stuck input cannot stall the whole domain.
  • History matters more than availability: auditors, stakeholders, or teammates need to see "which input was processed in run #37 and how long did it take?"

Do not use a Workflow when:

  • The process must stay alive continuously consuming from a queue or stream — use a Background Worker.
  • The process fires on a fixed UTC cadence with no per-input trigger — use a Cron Job.
  • The need is an HTTP service — use Web Service.
Info

Workflows sit between a Worker and a script: they have the control surface of a deployment (image, command, environment, timeout, retry) but the execution shape of a finite task. If "running for hours until stopped" sounds correct, lean Worker; if "run once with a watch timer" sounds correct, lean Workflow.

Configure the task#

Choose Workflow, provide the container image and command required for the task, then set the task timeout and retry limit. Each field has a distinct responsibility:

  • Container image — the image that contains the task program and its dependencies. This is commonly an image pushed from external CI rather than the workspace’s repository build; confirm the tag or digest is the one you intend to run.
  • Command — the invocation that defines what this workflow run *is*. For example python scripts/render_invoice.py --id $INVOICE_ID, node scripts/export.js, or a binary plus arguments. Keep the command deterministic: the same image plus the same command plus the same inputs should produce the same logical outcome, not timing-dependent jitter.
  • Timeout — the wall-clock limit after which the platform terminates an unresponsive run. Choose it for the worst plausible size of a healthy invocation (for example "rendering the largest invoice" or "importing the largest batch size your application allows") rather than for the average case.
  • Retry limit — the bounded number of retries after transient failures. Useful for temporary provider or network issues; harmful as a loop over bad inputs, so keep the limit small and the failure-classification inside the command honest.

Keep the command deterministic and make it safe to retry where possible. An import that can safely be restarted from the last externally observable checkpoint (a remote offset, a deterministically derived partition key) is less harmful to rerun than one that duplicates side effects per retry.

Tip

Encode input narrowly. A workflow task that takes an exact invoice, document, or job ID is easier to rerun and retry than one that implicitly scans an unbounded range. Prefer workflow --id=123 over "workflow scans for all pending jobs" when per-entity history matters.

Review runs#

The workflow screen records each invocation and its outcome — whether it succeeded, timed out, or hit the retry ceiling — with the exit status and application output captured per run. Inspect a failed run before retrying so a configuration or input problem is not repeated automatically.

What to look for when the last run failed:

  • Exit status — non-zero signals "the task reported failure," not "the platform did." Distinguish a deterministic bug in the input (which should be fixed before any retry) from transient infrastructure or provider throttling (where a retry may genuinely help).
  • Application output — the task’s own stdout/stderr plus the platform’s bookkeeping. The first error line in the earliest anomalous span is usually the root cause — later retries and wrapper timeout logs are often consequences.
  • Timeout vs crash — a timeout implies the task kept running but never completed within the limit; a crash often appears as an immediate non-zero exit with a short duration. Fix them differently: a timeout wants a longer timeout or a leaner input partitioning; a crash wants a code or environment repair.
Warning

Do not blindly retry a failed run with invalid input. A retry storm over a malformed document accumulates the recorded retry budget while producing only repeated failures.

  • Background Workers — continuous consumers with a "stay alive" model rather than per-run history.
  • Cron Jobs — scheduled, clock-driven finite runs in UTC where the cadence is the trigger, not the demand.
  • Logs — reading application output and stage information for a task that does not serve HTTP.
  • Deployments — observing the lineage of the workflow’s configuration alongside domain runs.
  • Services Overview — placing Workflows among the other service types before committing to the model.