Skip to main content

Stages & Stage Groups

Last updated on

A Stage is a major execution block within a pipeline. Each stage has its own runtime environment, caching configuration, failure strategies, and execution steps. Stages run sequentially by default, but can be configured for parallel execution or matrix-based fan-out.

Key Concept

In Harness 3.0, stages no longer require explicit type declarations like CI or Deployment. A stage is a container for steps, with deployment behavior configured through service and environment references.


Stage Schema

The Stage interface defines the complete structure of a stage in a v1 pipeline. Execution types (steps, approval, group, parallel, template, chain) are mutually exclusive — each stage uses exactly one.

stage-schema.ts
interface Stage {
// Identifiers
id: string // Stage identifier
name: string // Display name

// Execution types (mutually exclusive)
steps: Step[] // Regular step execution
approval: { uses: string; with: Record<string, any> } // Approval gate
group: { stages: Stage[] } // Grouped stages (sequential)
parallel: { stages: Stage[] } // Parallel stages
template: { uses: string; with: Record<string, any> } // Template reference
chain: { uses: string; with: Record<string, any> } // Chained pipeline

// Configuration
inputs: Input // Stage input variables
env: Record<string, string> // Stage environment variables
outputs: Record<string, any> // Output variables for inter-stage communication
clone: boolean | CloneConfig // Stage-level clone override
delegate: string | string[] // Delegate selector

// Runtime & Platform
runtime: "cloud" | "vm" | "kubernetes" | "shell" | RuntimeConfig
platform: { os: string; arch: string } // Target OS and architecture
workspace: boolean | { disabled: boolean; path: string }

// Resources
cache: { // Cache configuration
disabled: boolean
path: string | string[]
key: string
policy: "pull" | "pull-push" | "push"
}
volumes: Volume[] // Volume definitions

// Deployment targets
service: ServiceRef // Stage service target
environment: EnvironmentRef // Stage environment target
rollback: Step // Rollback step on failure

// Control flow
if: string // Conditional execution
disabled: boolean // Disable stage
on-failure: FailureStrategy // Failure handling
timeout: string // Max execution time
strategy: Strategy // Matrix/looping strategy
concurrency: ConcurrencyConfig // Concurrency controls
status: StatusConfig // Status check configuration

// GitHub Actions compatibility
needs: string | string[] // Stage dependencies
runs-on: string // Machine type
services: Record<string, Container> // Background services
permissions: Permissions // Stage permissions
}

Properties Reference

PropertyTypeDescription
idstringUnique identifier for the stage
namestringDisplay name for the stage
stepsStep[]List of steps for regular sequential execution
approval{ uses, with }Approval gate using a named provider and configuration
group{ stages: Stage[] }Sequential group of nested substages
parallel{ stages: Stage[] }Concurrent group of nested substages
template{ uses, with }Reference to a reusable stage template with inputs
chain{ uses, with }Triggers another pipeline as a child execution
inputsInputStage input variables for parameterization
envRecord<string, string>Stage-level environment variables available to all steps
outputsRecord<string, any>Output variables for inter-stage communication
cloneboolean | CloneConfigStage-level clone override for repository checkout
delegatestring | string[]Delegate selector for routing stage execution
runtimestring | RuntimeConfigRuntime infrastructure: cloud, vm, kubernetes, shell, or detailed config
platform{ os, arch }Target operating system and architecture
workspaceboolean | { disabled, path }Workspace configuration for shared filesystem
cacheCacheConfigCache configuration with path, key, and policy (pull, pull-push, push)
volumesVolume[]Volume definitions for shared storage between steps
serviceServiceRefService reference for deployment stages
environmentEnvironmentRefEnvironment reference for deployment stages
rollbackStepRollback step executed on stage failure
ifstringExpression that controls conditional execution
disabledbooleanWhen true, the stage is skipped without removing it from the YAML
on-failureFailureStrategyFailure handling strategy with error matching and actions
timeoutstringMaximum execution time (e.g., 30m, 2h)
strategyStrategyMatrix, for-loop, or while-loop execution strategy
concurrencyConcurrencyConfigConcurrency controls for parallel execution limits
statusStatusConfigStatus check configuration for the stage
needsstring | string[]Stage dependencies (GitHub Actions compatibility)
runs-onstringMachine type selector (GitHub Actions compatibility)
servicesRecord<string, Container>Background service containers for the stage
permissionsPermissionsStage-level permissions (GitHub Actions compatibility)

Stage Types

There are six functional patterns for stages. The execution types are mutually exclusive — each stage uses exactly one of steps, approval, template, chain, group, or parallel.

1. Steps Stage (Default)

The default stage type. Contains a list of steps that execute sequentially.

steps-stage.yaml
stages:
- name: build
steps:
- run: npm install
- run: npm run build
- run: npm test

2. Approval Stage

Pauses the pipeline and waits for manual or automated approval before proceeding.

approval-stage.yaml
stages:
- name: approve-deploy
approval:
uses: harness
with:
approvers:
users:
- admin@company.com
minimum: 1
message: "Please approve production deployment"
timeout: 24h

3. Template Stage

References a reusable stage template with parameterized inputs.

template-stage.yaml
stages:
- name: deploy-staging
template:
uses: account.deploy-template@1.2.0
with:
environment: staging
replicas: 2

4. Chain Stage

Triggers another pipeline as a child execution, passing context and inputs.

chain-stage.yaml
stages:
- name: run-integration-tests
chain:
uses: integration-test-pipeline
with:
build_number: ${{ stages.build.outputs.BUILD_NUMBER }}

5. Group Stage

Organizes multiple substages into a sequential group with shared configuration.

group-stage.yaml
stages:
- name: deploy-all
group:
stages:
- name: deploy-staging
steps:
- run: ./deploy.sh staging
- name: deploy-production
steps:
- run: ./deploy.sh production

6. Parallel Stage

Runs multiple substages concurrently. The pipeline waits for all to complete before continuing.

parallel-stage.yaml
stages:
- name: test-all
parallel:
stages:
- name: unit-tests
steps:
- run: npm run test:unit
- name: integration-tests
steps:
- run: npm run test:integration

Stage Groups

Stage groups organize related stages together and allow shared configuration like failure strategies or conditionals to apply to all stages in the group.

Basic Group

stage-group.yaml
stages:
- name: integration-tests
group:
stages:
- name: api-tests
steps:
- run: npm run test:api
- name: e2e-tests
steps:
- run: npm run test:e2e
- name: performance-tests
steps:
- run: npm run test:perf

Conditional Group

Apply a condition to an entire group. When the condition evaluates to false, all stages in the group are skipped.

conditional-group.yaml
stages:
- name: build
steps:
- run: npm run build
- name: deploy-stages
if: ${{ trigger.branch == "main" }}
group:
stages:
- name: deploy-staging
steps:
- run: ./deploy.sh staging
- name: deploy-production
steps:
- run: ./deploy.sh production

Group with Shared Failure Strategy

Apply a failure strategy to the entire group so all substages inherit the same behavior.

group-failure-strategy.yaml
stages:
- name: deployment-pipeline
on-failure:
action:
manual-intervention:
timeout: 1h
timeout-action: abort
group:
stages:
- name: deploy-staging
steps:
- run: ./deploy.sh staging
- name: run-smoke-tests
steps:
- run: npm run test:smoke
- name: deploy-production
steps:
- run: ./deploy.sh production

Parallel Execution

Use the parallel: keyword to run multiple stages concurrently. All stages in a parallel block start simultaneously, and the pipeline waits for all to complete before continuing.

parallel-stages.yaml
stages:
- name: build
steps:
- run: npm run build
- name: test-all
parallel:
stages:
- name: unit-tests
steps:
- run: npm run test:unit
- name: integration-tests
steps:
- run: npm run test:integration
- name: lint
steps:
- run: npm run lint
- name: deploy
steps:
- run: ./deploy.sh
Parallel Performance

Parallel stages each get their own runtime environment and can run on different machines or containers simultaneously, significantly reducing total pipeline execution time.

Individual stages within a parallel block can also have their own conditions.

parallel-conditional.yaml
stages:
- name: quality-checks
parallel:
stages:
- name: unit-tests
steps:
- run: npm run test:unit
- name: security-scan
if: ${{ trigger.branch == "main" }}
steps:
- run: npm run security:scan
- name: license-check
steps:
- run: npm run license:check

Matrix Strategy

The strategy field supports three looping patterns: matrix, for, and while.

Matrix

Creates stage instances for each combination of matrix variables. Use include to add extra combinations and exclude to remove specific ones.

matrix-strategy.yaml
stages:
- name: test
strategy:
matrix:
go: ["1.21", "1.22", "1.23"]
os: [linux, macos, windows]
include:
- go: "1.23"
os: linux
experimental: true
exclude:
- go: "1.21"
os: windows
max-parallel: 4
fail-fast: true
steps:
- run: echo "Testing Go ${{ matrix.go }} on ${{ matrix.os }}"

Use max-parallel to limit concurrency and fail-fast to cancel remaining instances on the first failure.

For Loop

Iterate a stage a fixed number of times. Access the current iteration index via ${{ for.iteration }}.

for-loop.yaml
stages:
- name: deploy
strategy:
for:
iterations: 3
steps:
- run: echo "Iteration ${{ for.iteration }}"

While Loop

Repeat a stage while a condition evaluates to true. Set iterations as a safety bound on maximum repetitions.

while-loop.yaml
stages:
- name: wait-for-healthy
strategy:
while:
iterations: 10
condition: ${{ steps.health_check.output.STATUS }} != "healthy"
steps:
- name: health_check
run: curl -s https://api.example.com/health

Conditional Execution

Expression-Based Conditions

conditional-stages.yaml
stages:
- name: build
steps:
- run: npm run build
- name: deploy-staging
if: ${{ trigger.branch == "develop" }}
steps:
- run: ./deploy.sh staging
- name: deploy-production
if: ${{ trigger.branch == "main" && trigger.event == "push" }}
steps:
- run: ./deploy.sh production
- name: notify
if: always()
steps:
- run: ./send-notification.sh

Use always() to run a stage regardless of previous outcomes, failure() to run only when a previous stage failed, and success() (the default) to run only when all previous stages succeeded.

Disabled Stage

Set disabled: true to skip a stage without removing it from the YAML. Useful for debugging or temporary suppression.

disabled-stage.yaml
stages:
- name: build
steps:
- run: npm run build
- name: experimental-deploy
disabled: true
steps:
- run: ./deploy-experimental.sh

Failure Strategies

Define how a stage handles failures using the on-failure property. Strategies can match specific error types and define actions including retry, manual intervention, and rollback.

Error Types

Error TypeDescription
allMatch all error types
timeoutStep or stage exceeded its configured timeout
authenticationAuthentication failure
authorizationAuthorization failure
connectivityNetwork connectivity issue
delegate-provisioningDelegate provisioning failure
input-timeoutInput approval timed out
verificationVerification step failure
unknownUnknown error type

Action Types

ActionDescription
abortAbort the pipeline
failMark as failed (default behavior)
ignoreIgnore the failure and continue
retryRetry the step or stage
manual-interventionPause for manual intervention
stage-rollbackRollback the stage
pipeline-rollbackRollback the entire pipeline
successMark as success despite failure

Ignore Failures

ignore-failure.yaml
on-failure:
action: ignore

Retry with Configuration

retry-config.yaml
on-failure:
errors:
- connectivity
- timeout
action:
retry:
attempts: 3
interval:
- "10s"
- "30s"
- "1m"
failure-action: abort

Manual Intervention

manual-intervention.yaml
on-failure:
action:
manual-intervention:
timeout: 1h
timeout-action: abort

Match Specific Exit Codes

exitcode-failure.yaml
on-failure:
exitcode: "1"
action: ignore
Failure Propagation

If no failure strategy is defined, stage failures propagate up to the pipeline level. The pipeline will be marked as failed and subsequent stages will not execute unless they have an if: always() condition.