Skip to content

Example Showcase

Six curated examples that demonstrate OrchStep’s core capabilities. Each example shows the YAML source, the actual execution output (captured from orchstep run), the spec test assertions, and the verification results.


The simplest OrchStep workflow — one task, one step.

# Example: Hello World
# The simplest OrchStep workflow -- one task, one step.
# This is the starting point for learning OrchStep.
#
# Try: orchstep run
name: hello-world
desc: "Your first OrchStep workflow"
tasks:
main:
desc: "Say hello"
steps:
- name: greet
desc: "Output a greeting using the shell function"
func: shell
do: echo 'Hello from OrchStep!'

OrchStep resolves variables with 4-level precedence: Runtime > Step > Task > Definition.

# Example: Variable Precedence
# Full demonstration of how OrchStep resolves variables when the same
# name is defined at multiple levels. Higher levels always win:
#
# Runtime (--var) > Step vars > Task vars > Definition vars
#
# Try: orchstep run
name: variable-precedence-demo
desc: "Shows how variables at different levels override each other"
# Definition variables (lowest priority in-file)
defaults:
environment: "global-default"
region: "us-east-1"
replicas: "1"
multi_level: "from-definition"
app_name: "my-app"
tasks:
show_definition_vars:
desc: "Display definition-level defaults"
steps:
- name: show_globals
func: shell
do: |
echo "environment = {{ vars.environment }}"
echo "region = {{ vars.region }}"
echo "replicas = {{ vars.replicas }}"
show_task_override:
desc: "Task vars override definition vars"
vars:
environment: "task-staging"
replicas: "3"
task_only: "only-visible-in-this-task"
steps:
- name: show_task_vars
func: shell
do: |
echo "environment = {{ vars.environment }}"
echo "replicas = {{ vars.replicas }}"
echo "region = {{ vars.region }}"
echo "task_only = {{ vars.task_only }}"
show_step_override:
desc: "Step vars override task vars"
vars:
environment: "task-qa"
replicas: "5"
steps:
- name: step_with_overrides
desc: "This step overrides environment and replicas"
vars:
environment: "step-production"
replicas: "10"
func: shell
do: |
echo "=== Inside step with overrides ==="
echo "environment = {{ vars.environment }}"
echo "replicas = {{ vars.replicas }}"
- name: step_without_overrides
desc: "This step inherits from the task level"
func: shell
do: |
echo "=== Inside step without overrides ==="
echo "environment = {{ vars.environment }}"
echo "replicas = {{ vars.replicas }}"
show_template_vars:
desc: "Variables can use template expressions"
vars:
base_url: "https://api.example.com"
api_version: "v2"
steps:
- name: computed_variable
desc: "Build a URL from other variables"
vars:
full_url: "{{ vars.base_url }}/{{ vars.api_version }}/users"
func: shell
do: |
echo "Base URL: {{ vars.base_url }}"
echo "API Version: {{ vars.api_version }}"
echo "Full URL: {{ vars.full_url }}"
main:
desc: "Run all precedence demonstrations"
steps:
- name: demo_definition
task: show_definition_vars
- name: demo_task
task: show_task_override
- name: demo_step
task: show_step_override
- name: demo_template
task: show_template_vars
- name: summary
func: shell
do: |
echo "=== Variable Precedence (highest to lowest) ==="
echo " 1. Runtime (--var key=value)"
echo " 2. Step vars"
echo " 3. Task vars"
echo " 4. Definition vars (defaults:)"

Resilient error handling with jitter to prevent thundering herd problems.

# Example: Retry with Exponential Backoff and Jitter
# Shows how to prevent "thundering herd" problems by adding
# random jitter to retry delays.
#
# jitter: 0.0 = no randomness (deterministic delays)
# jitter: 0.3 = +/-30% variation around the calculated delay
# jitter: 0.5 = +/-50% variation
# jitter: 1.0 = +/-100% variation (0 to 2x the delay)
#
# Try: orchstep run resilient-deploy
# Try: orchstep run api-with-backoff
name: retry-with-jitter-demo
desc: "Resilient retry with jitter to prevent thundering herd"
tasks:
# -- Basic jitter for distributed retries --
resilient-deploy:
desc: "Deploy with jitter to avoid retry storms"
steps:
- name: deploy_to_cluster
func: shell
do: |
echo "Deploying application to cluster..."
# In real usage: kubectl apply -f deployment.yml
echo "Deployment successful"
retry:
max_attempts: 5
interval: 2s
jitter: 0.3 # +/-30% variation: delays between 1.4s and 2.6s
# -- Exponential backoff with jitter --
api-with-backoff:
desc: "API calls with exponential backoff and jitter"
steps:
- name: call_rate_limited_api
func: shell
do: |
echo "Calling rate-limited API..."
# In real usage: curl https://api.example.com/data
echo "Response received"
retry:
max_attempts: 5
interval: 100ms
backoff_rate: 2.0 # Double the delay each time
jitter: 0.3 # +/-30% jitter on each backoff delay
# Approximate delays:
# Attempt 1->2: ~100ms (70ms - 130ms)
# Attempt 2->3: ~200ms (140ms - 260ms)
# Attempt 3->4: ~400ms (280ms - 520ms)
# Attempt 4->5: ~800ms (560ms - 1040ms)
# -- Jitter with max delay cap --
capped-backoff:
desc: "Backoff with jitter capped at a maximum delay"
steps:
- name: sync_to_remote
func: shell
do: |
echo "Syncing data to remote storage..."
echo "Sync complete"
retry:
max_attempts: 5
interval: 100ms
backoff_rate: 3.0 # Triple each time (aggressive backoff)
max_delay: 500ms # Never wait more than 500ms
jitter: 0.5 # +/-50% on the capped delay
# -- Multiple steps with independent jitter --
parallel-service-calls:
desc: "Each step retries with its own jitter pattern"
steps:
- name: call_auth_service
func: shell
do: echo "Authenticating with auth service..."
retry:
max_attempts: 3
interval: 100ms
jitter: 0.3
on_error: warn
- name: call_data_service
func: shell
do: echo "Fetching data from data service..."
retry:
max_attempts: 3
interval: 200ms
jitter: 0.5
on_error: warn
- name: aggregate_results
func: shell
do: |
echo "Aggregating service responses..."
echo "Auth status: {{ steps.call_auth_service.status }}"
echo "Data status: {{ steps.call_data_service.status }}"

Built-in HTTP function with response parsing and variable-driven URLs.

# Example: Basic HTTP GET Requests
# Shows how to make GET requests with the built-in http function.
#
# The http function supports two syntaxes:
# - Short form: do: "GET https://api.example.com/data"
# - Args form: args: { url: "...", method: GET }
#
# Response data is available via:
# result.status_code - HTTP status code (200, 404, etc.)
# result.body - Raw response body as string
# result.data_object - Auto-parsed JSON response (if JSON)
# result.headers - Response headers map
# result.url - Requested URL
# result.method - HTTP method used
#
# Try: orchstep run fetch-api-data
name: basic-get-request-demo
desc: "HTTP GET requests with variable URLs and response parsing"
defaults:
api_base: "https://httpbin.org"
tasks:
# -- Simple GET request --
fetch-api-data:
desc: "Fetch data from a REST API"
steps:
- name: get_users
func: http
do: "GET {{ vars.api_base }}/get"
outputs:
status: "{{ result.status_code }}"
url: "{{ result.url }}"
method: "{{ result.method }}"
- name: show_result
func: shell
do: |
echo "Status: {{ steps.get_users.status }}"
echo "URL: {{ steps.get_users.url }}"
echo "Method: {{ steps.get_users.method }}"
# -- Parse JSON response --
parse-json-response:
desc: "Access fields from a JSON response body"
steps:
- name: get_json_data
func: http
do: "GET {{ vars.api_base }}/json"
outputs:
author: "{{ result.data_object.slideshow.author }}"
title: "{{ result.data_object.slideshow.title }}"
- name: display_parsed_data
func: shell
do: |
echo "Author: {{ steps.get_json_data.author }}"
echo "Title: {{ steps.get_json_data.title }}"

Build frontend and backend concurrently, then deploy with merged outputs.

# Example: Parallel Step Execution
# Demonstrates: Running multiple steps concurrently using the parallel: block
#
# Key concepts:
# - Steps inside parallel: run simultaneously
# - Each parallel step can produce outputs
# - Outputs merge back for subsequent sequential steps
# - If any parallel step fails, the block fails
#
# Try: orchstep run build-and-deploy
name: parallel-build-demo
desc: "Build frontend and backend in parallel, then deploy"
defaults:
version: "2.0.0"
environment: staging
tasks:
build-and-deploy:
desc: "Parallel build followed by sequential deploy"
steps:
- name: build_all
desc: "Build both services in parallel"
parallel:
- name: build_frontend
func: shell
do: |
echo "Building frontend v{{ vars.version }}..."
echo "Compiling TypeScript..."
echo "Bundling assets..."
echo "FRONTEND_ARTIFACT=frontend-{{ vars.version }}.js"
outputs:
artifact: '{{ result.output | regexFind "FRONTEND_ARTIFACT=(.+)" }}'
- name: build_backend
func: shell
do: |
echo "Building backend v{{ vars.version }}..."
echo "Compiling Go..."
echo "Running unit tests..."
echo "BACKEND_ARTIFACT=backend-{{ vars.version }}"
outputs:
artifact: '{{ result.output | regexFind "BACKEND_ARTIFACT=(.+)" }}'
- name: deploy
desc: "Deploy both artifacts"
func: shell
do: |
echo "Deploying to {{ vars.environment }}..."
echo "Frontend: {{ steps.build_frontend.artifact }}"
echo "Backend: {{ steps.build_backend.artifact }}"
echo "Deploy complete!"

A realistic production pipeline: build, test, assert, deploy, health-check, and smoke-test.

# Example: Deploy Pipeline
# A realistic deployment pipeline that combines multiple OrchStep features:
# - Variables for configuration
# - Shell commands for build and deploy
# - HTTP health checks with retry
# - Assertions to validate success
#
# Try: orchstep run
# Try: orchstep run --var environment=staging --var version=v2.1.0
name: deploy-pipeline
desc: "Production deployment pipeline with health checks and validation"
defaults:
app_name: "payments-api"
version: "v1.0.0"
environment: "production"
registry: "registry.example.com"
replicas: 3
health_check_url: "https://payments-api.example.com/health"
rollback_on_failure: true
tasks:
# --- Step 1: Build ---
build:
desc: "Build the container image"
steps:
- name: compile
func: shell
do: |
echo "=== Build Phase ==="
echo "Building {{ vars.app_name }}:{{ vars.version }}"
echo "Build completed at $(date +%H:%M:%S)"
outputs:
image_tag: "{{ vars.registry }}/{{ vars.app_name }}:{{ vars.version }}"
- name: run_tests
func: shell
do: |
echo "Running unit tests..."
echo "42 tests passed, 0 failed"
outputs:
tests_passed: 42
tests_failed: 0
- name: verify_tests
func: assert
args:
conditions:
- condition: "steps.run_tests.tests_failed === 0"
desc: "No test failures"
- condition: "steps.run_tests.tests_passed > 0"
desc: "At least one test ran"
- name: push_image
func: shell
do: |
echo "Pushing {{ steps.compile.image_tag }}"
echo "Image pushed successfully"
# --- Step 2: Deploy ---
deploy:
desc: "Deploy to the target environment"
steps:
- name: pre_deploy_check
func: shell
do: |
echo "=== Deploy Phase ==="
echo "Environment: {{ vars.environment }}"
echo "Replicas: {{ vars.replicas }}"
- name: rolling_deploy
func: shell
do: |
echo "Starting rolling deployment..."
echo "Scaling {{ vars.app_name }} to {{ vars.replicas }} replicas"
echo "Deployment ID: deploy-{{ vars.environment }}-$(date +%s)"
echo "All replicas healthy"
outputs:
deploy_id: "deploy-{{ vars.environment }}-001"
status: "deployed"
# --- Step 3: Verify ---
verify:
desc: "Health check and validation"
steps:
- name: health_check
desc: "Check service health (retries on failure)"
func: shell
do: |
echo "Checking health at {{ vars.health_check_url }}"
echo "Status: 200 OK"
echo "Response: {\"status\":\"healthy\",\"version\":\"{{ vars.version }}\"}"
outputs:
http_status: 200
response: '{"status":"healthy","version":"{{ vars.version }}"}'
retry:
max: 5
delay: 3
backoff: 2
- name: validate_health
func: assert
args:
conditions:
- condition: "steps.health_check.http_status === 200"
desc: "Health endpoint returns 200"
- condition: '{{ contains "healthy" steps.health_check.response }}'
desc: "Service reports healthy status"
- name: smoke_test
func: shell
do: |
echo "Running smoke tests against {{ vars.environment }}..."
echo "POST /api/payments -- 201 Created"
echo "GET /api/payments/1 -- 200 OK"
echo "All smoke tests passed"
# --- Orchestrator ---
main:
desc: "Run the full deploy pipeline"
steps:
- name: run_build
task: build
- name: run_deploy
task: deploy
- name: run_verify
task: verify
- name: report
func: shell
do: |
echo ""
echo "=========================================="
echo " Deployment Complete"
echo "=========================================="
echo " App: {{ vars.app_name }}"
echo " Version: {{ vars.version }}"
echo " Environment: {{ vars.environment }}"
echo " Replicas: {{ vars.replicas }}"
echo "=========================================="