Skip to content
Docs

Workflow Concepts

Overview

Workflows in Hyve are automated deployment pipelines defined as YAML files in Git repositories. They enable consistent, repeatable operations with built-in validation, secret management, and variable substitution.

Automated Pipelines

Define multi-step deployment processes

Requirements Validation

Ensure tools and secrets are available before execution

Variable Substitution

Use environment and workflow variables

Version Controlled

Workflows stored in Git with full history

Workflow Structure

Workflows follow a standard YAML structure:

apiVersion: v1
kind: Workflow
metadata:
name: deploy-app
description: Deploy application to Kubernetes
tools:
- name: kubectl
min: "1.28.0"
spec:
requirements:
secrets:
- name: DOCKER_TOKEN
provider: docker
env:
APP_NAME: my-app
NAMESPACE: default
jobs:
- name: deploy
steps:
- name: apply-manifests
command: kubectl apply -f manifests/

Metadata

metadata.name string required

Unique workflow identifier

metadata.description string

Human-readable description of the workflow

Spec

spec.inputs array

Variables that must be present before the workflow runs. When the workflow is executed as a lifecycle hook (beforeCreate, onCreate, onDelete, afterDelete, preReconcile), values are injected automatically by the reconciler. When run ad-hoc via hyve workflow run, any missing inputs are prompted for in the TUI or must be supplied with --set KEY=VALUE on the CLI. See Workflow Inputs for full details.

spec.preFlight.cluster string

Set to "skip" to bypass the kubeconfig setup step before execution. Required for beforeCreate workflows that need to authenticate to a cloud provider before the cluster exists.

spec.requirements object

Prerequisites for workflow execution (tools and secrets)

spec.env object

Environment variables available to all jobs

spec.jobs array required

List of jobs to execute sequentially

Jobs and Steps

Jobs contain steps that execute commands:

jobs:
- name: build
steps:
- name: docker-build
command: docker build -t ${APP_NAME}:latest .
- name: docker-push
command: docker push ${APP_NAME}:latest
- name: deploy
dependsOn: [build]
steps:
- name: kubectl-apply
command: kubectl apply -f manifests/

Job Fields

jobs[].name string required

Job identifier

jobs[].dependsOn array

List of job names that must complete before this job runs

jobs[].steps array required

List of steps to execute in sequence

Step Fields

steps[].name string required

Step identifier

steps[].command string

Single command to execute

steps[].script string

Multi-line shell script to execute

Requirements

Workflows can validate prerequisites before execution:

Tool Requirements

Declare required CLI tools under spec.requirements.tools. A version is optional — a bare name: entry means any installed version is acceptable; version: (if set) is checked against whatever <tool> --version/-v/version reports, matching or newer.

spec:
requirements:
tools:
- name: kubectl
version: "1.28.0"
- name: helm
version: "3.12.0"
- name: docker # any version — just needs to be present

Checked with a plain PATH lookup (exec.LookPath) before the workflow runs — this is a per-workflow, self-contained check. There’s no hyve.yaml-level tools list it cross-references and no coordination across workflows; each workflow’s requirements.tools stands alone. There is no separate hyve-level tools manifest today — see the Server Mode guide for the recommended way to make sure a hyve serve container actually has what it needs (bake tools into the image at build time; an admin-triggered workflow to patch a running container as the fallback).

Secret Requirements

Load secrets from the environment:

spec:
requirements:
secrets:
- name: DOCKER_TOKEN
required: true
description: Docker Hub authentication
- name: GITHUB_TOKEN
required: false
description: GitHub API token (optional)

Checked with os.Getenv — if a required secret’s environment variable isn’t set, the workflow fails before running with a clear error. provider is optional and only changes the suggestion text in that error for a handful of recognized values (civo, aws, gcp, azure — e.g. suggesting aws configure); it doesn’t look anything up on its own.

Workflow Inputs

spec.inputs declares the environment variables a workflow needs at runtime. This is distinct from spec.requirements.secrets (which loads secrets from storage) — inputs are values the caller must supply.

spec:
inputs:
- name: HYVE_CLUSTER_NAME
description: "Name of the cluster to provision infrastructure for"
- name: HYVE_CLUSTER_REGION
description: "Cloud region to create resources in"
- name: HYVE_CLUSTER_PROVIDER
description: "Cloud provider (azure, aws, gcp, civo)"
jobs:
- name: provision-network
steps:
- name: create-vnet
command: |
az network vnet create \
--name ${HYVE_CLUSTER_NAME}-vnet \
--location ${HYVE_CLUSTER_REGION} \
--resource-group ${HYVE_CLUSTER_NAME}-rg

Automatic injection by lifecycle hooks

All lifecycle hooks receive the following HYVE_* variables from the cluster definition automatically:

VariableSourceAvailable in
HYVE_CLUSTER_NAMEmetadata.nameAll hooks
HYVE_CLUSTER_REGIONmetadata.regionAll hooks
HYVE_CLUSTER_PROVIDERspec.providerAll hooks
HYVE_CLUSTER_TYPEspec.clusterTypeAll hooks
HYVE_CLUSTER_K8S_VERSIONspec.kubernetesVersionAll hooks
HYVE_AWS_ACCOUNTspec.awsAccountAll hooks (AWS)
HYVE_GCP_PROJECTspec.gcpProjectAll hooks (GCP)
HYVE_AZURE_SUBSCRIPTIONspec.azureSubscriptionAll hooks (Azure)
HYVE_CIVO_ORGspec.civoOrganizationAll hooks (Civo)
HYVE_CLUSTER_ACCOUNT_IDresolved AWS account IDAll hooks (AWS)
HYVE_CLUSTER_VPC_IDspec.awsVpcIdAll hooks (AWS)
Provider credentialsprovider-configs/*.yamlAll hooks

Only the account variable that matches the cluster’s provider is set — the others are left unset.

onCreate, afterCreate, and onDelete hooks additionally receive live cluster data:

VariableDescription
HYVE_CLUSTER_IP_ADDRESSCluster API endpoint IP
HYVE_CLUSTER_ACCESS_PORTCluster API port
HYVE_CLUSTER_IDCloud provider cluster ID
HYVE_CLUSTER_STATUSCluster status string
HYVE_CLUSTER_KUBECONFIGPath to cluster kubeconfig
KUBECONFIGSame as HYVE_CLUSTER_KUBECONFIG
HYVE_CLUSTER_OIDC_URLEKS OIDC provider URL (AWS only)

beforeCreate and afterDelete hooks run without a live cluster — kubeconfig injection is skipped.

Output variables printed by a beforeCreate step in HYVE_KEY=value format are captured by the reconciler and written back to the cluster definition before creation proceeds. Supported output variables include:

Output VariableEffect
HYVE_VPC_IDSets spec.awsVpcId
HYVE_EKS_ROLE_NAMESets spec.awsEksRoleName
HYVE_NODE_ROLE_NAMESets spec.awsNodeRoleName
HYVE_EKS_ROLE_ARNSets spec.awsEksRoleArn
HYVE_NODE_ROLE_ARNSets spec.awsNodeRoleArn
HYVE_CLUSTER_SG_IDSets spec.awsClusterSgId
HYVE_WORKER_SG_IDSets spec.awsWorkerSgId

Ad-hoc runs

When the same workflow is run outside a cluster lifecycle (e.g., to manually provision infrastructure), missing inputs are handled as follows:

CLI — supply with --set:

Terminal window
hyve workflow run provision-network \
--set HYVE_CLUSTER_NAME=my-cluster \
--set HYVE_CLUSTER_REGION=eastus \
--set HYVE_CLUSTER_PROVIDER=azure

TUI — missing inputs are prompted for interactively. Variables already present in the environment are skipped automatically.

inputs[].name string required

Environment variable name (e.g. HYVE_CLUSTER_NAME)

inputs[].description string

Human-readable label shown in the TUI prompt and in hyve workflow show output

inputs[].default string

Default value used when no value is supplied. Shown as placeholder in the TUI prompt.

Environment Variables

Define variables at workflow or job level:

Workflow-Level Variables

Available to all jobs:

spec:
env:
APP_NAME: my-app
REGISTRY: docker.io/myuser
VERSION: v1.0.0
jobs:
- name: build
steps:
- name: build
command: docker build -t ${REGISTRY}/${APP_NAME}:${VERSION} .

Job-Level Variables

Available only to specific job:

jobs:
- name: deploy
env:
NAMESPACE: production
REPLICAS: "3"
steps:
- name: deploy
command: kubectl scale deployment ${APP_NAME} --replicas=${REPLICAS} -n ${NAMESPACE}

Variable Substitution

Hyve supports full shell variable substitution:

steps:
- name: example
command: echo "Deploying ${APP_NAME} version ${VERSION:-latest} to ${NAMESPACE}"

Running Workflows

Basic Execution

Terminal window
# Run workflow
hyve workflow run deploy-app
# Run with specific cluster
hyve workflow run deploy-app --cluster production

Lifecycle Workflows

Workflows can be triggered automatically during cluster lifecycle events:

In Templates

# Template definition
apiVersion: v1
kind: Template
metadata:
name: prod-template
spec:
# ... cluster config
workflows:
beforeCreate:
- provision-vpc
onCreate:
- setup-monitoring
- deploy-app
afterCreate:
- create-app-secrets
onDelete:
- backup-data
afterDelete:
- cleanup-vpc

In Cluster Definitions

# Cluster definition
apiVersion: v1
kind: Cluster
metadata:
name: production
region: NYC1
spec:
# ... cluster config
workflows:
beforeCreate:
- provision-network
onCreate:
- setup-monitoring
afterCreate:
- create-app-secrets
onDelete:
- backup-data
afterDelete:
- cleanup-network
HookWhen It RunsCluster Exists?Kubeconfig Available?Typical Use Cases
beforeCreateBefore cluster is createdNoNoProvision VPC, create IAM roles, allocate IP ranges
onCreateAfter cluster is ready, before spec.resources appliesYesYesSetup monitoring, deploy apps, configure services
afterCreateAfter cluster is ready, after spec.resources has appliedYesYesCreate a Secret a resource-managed Deployment references, DNS pointing at a now-deployed app
onDeleteBefore cluster is deletedYesYesBackup data, export logs, cleanup external resources
afterDeleteAfter cluster is deletedNoNoDestroy VPC, remove IAM roles, release IP ranges
preReconcileWhen param drift is detected on an ACTIVE clusterYesYesRefresh credentials, sync config before scaling

Remote Workflow References

Every entry above can also be a remote reference instead of a local name — a workflow pulled from another Git repository, resolved and content-hashed into hyve.lock, the same way modules are referenced via spec.driver.source:

spec:
workflows:
onCreate:
- pre-flight-checks # local, unchanged
- source: github.com/myorg/shared-workflows//setup-monitoring.yaml@v1.2.0 # remote, pinned
- source: github.com/myorg/shared-workflows//setup-monitoring.yaml # remote, tracks latest

This exists so shared operational workflows — monitoring setup, backup jobs, teardown routines — can live in one repository and be referenced from many, instead of being copied into every state repository that needs them. Version is optional: omit it and Hyve resolves the latest commit once and locks the resolved SHA, exactly like an unversioned module reference.

A lifecycle hook entry must resolve to a single workflow file — a directory-form source (used for bulk-installing a whole set of workflows via hyve workflow install) is rejected if placed directly in a hook. See hyve workflow for the full source-string format, path resolution rules, and name-resolution order for hyve workflow run.

Auth-Bootstrap Workflows (preFlight.cluster: skip)

Some beforeCreate workflows need to authenticate to a cloud provider to provision resources (e.g. create an EKS IAM role or VPC) before the cluster exists. By default Hyve tries to set up kubeconfig before running any workflow. Set preFlight.cluster: skip in the workflow spec to bypass the kubeconfig setup step:

apiVersion: v1
kind: Workflow
metadata:
name: provision-iam-roles
spec:
preFlight:
cluster: skip # skip EKS DescribeCluster + kubeconfig sync
jobs:
- name: create-roles
steps:
- name: create-eks-role
script: |
aws iam create-role --role-name eks-cluster-role \
--assume-role-policy-document file://eks-trust-policy.json
echo "HYVE_EKS_ROLE_NAME=eks-cluster-role"

The HYVE_EKS_ROLE_NAME=eks-cluster-role output line is captured by the reconciler and written to spec.awsEksRoleName before the cluster is created.

Execution Flow

Load Workflow

Read workflow YAML from repository

workflows/deploy-app.yaml

Validate Requirements

Check tools and secrets

[INFO] Validating workflow requirements...
[INFO] ✅ All requirements validated successfully

Set Environment

Load environment variables and secrets

Terminal window
export APP_NAME=my-app
export DOCKER_TOKEN=***

Execute Jobs

Run jobs in sequence

[INFO][build] Starting job 'build'
[INFO][build][docker-build] Running step 'docker-build'

Report Results

Show success or failure

[INFO] Workflow 'deploy-app' completed successfully

Workflow Types

Deployment Workflow

Deploy applications to Kubernetes:

apiVersion: v1
kind: Workflow
metadata:
name: deploy-app
tools:
- name: kubectl
min: "1.28.0"
spec:
env:
APP_NAME: my-app
jobs:
- name: deploy
steps:
- name: apply
command: kubectl apply -f manifests/
- name: wait
command: kubectl rollout status deployment/${APP_NAME}

Build Workflow

Build and push Docker images:

apiVersion: v1
kind: Workflow
metadata:
name: build-image
tools:
- name: docker
spec:
requirements:
secrets:
- name: DOCKER_TOKEN
provider: docker
env:
IMAGE: myapp
TAG: latest
jobs:
- name: build
steps:
- name: docker-build
script: |
echo "$DOCKER_TOKEN" | docker login -u myuser --password-stdin
docker build -t ${IMAGE}:${TAG} .
docker push ${IMAGE}:${TAG}

Infrastructure Workflow

Provision infrastructure with Terraform:

apiVersion: v1
kind: Workflow
metadata:
name: provision-infra
tools:
- name: terraform
min: "1.5.0"
spec:
requirements:
secrets:
- name: AWS_ACCESS_KEY_ID
provider: aws
- name: AWS_SECRET_ACCESS_KEY
provider: aws
jobs:
- name: provision
steps:
- name: init
command: terraform init
- name: plan
command: terraform plan
- name: apply
command: terraform apply -auto-approve

Commands vs Scripts

Single Commands

For simple operations:

steps:
- name: deploy
command: kubectl apply -f manifests/

Multi-Line Scripts

For complex operations:

steps:
- name: deploy
script: |
echo "Starting deployment..."
kubectl apply -f manifests/
kubectl rollout status deployment/my-app
echo "Deployment complete!"

Job Dependencies

Control execution order with dependencies:

jobs:
- name: test
steps:
- name: run-tests
command: npm test
- name: build
dependsOn: [test]
steps:
- name: docker-build
command: docker build -t myapp .
- name: deploy
dependsOn: [build]
steps:
- name: kubectl-apply
command: kubectl apply -f manifests/

Execution order: testbuilddeploy

Error Handling

Workflows stop on first error:

jobs:
- name: deploy
steps:
- name: test
command: npm test # If this fails, workflow stops
- name: deploy
command: kubectl apply -f manifests/ # This won't run

Best Practices

1. Use Descriptive Names

Name workflows, jobs, and steps clearly:

metadata:
name: deploy-api-production
description: Deploy API service to production cluster
jobs:
- name: run-tests
steps:
- name: unit-tests
- name: integration-tests
2. Declare Tool Requirements

Declare required tools at the top level with optional version constraints:

tools:
- name: kubectl
min: "1.28.0"
spec:
requirements:
secrets:
- name: DOCKER_TOKEN
provider: docker
required: true
3. Use Environment Variables

Avoid hardcoding values:

# Good
env:
NAMESPACE: production
steps:
- name: deploy
command: kubectl apply -f manifests/ -n ${NAMESPACE}
# Bad
steps:
- name: deploy
command: kubectl apply -f manifests/ -n production
4. Document Workflows

Add descriptions explaining purpose:

metadata:
name: deploy-app
description: |
Deploy application to Kubernetes cluster
- Runs tests first
- Builds Docker image
- Deploys to cluster
- Waits for rollout
5. Test in Development

Test workflows in development before production:

Terminal window
# Switch to development
hyve git use development
# Test workflow
hyve workflow run deploy-app --cluster dev-cluster
# If successful, run in production
hyve git use production
hyve workflow run deploy-app --cluster prod-cluster

Workflow Examples

Complete Deployment Pipeline

apiVersion: v1
kind: Workflow
metadata:
name: complete-pipeline
description: Full CI/CD pipeline
tools:
- name: docker
- name: kubectl
min: "1.28.0"
- name: helm
min: "3.12.0"
spec:
requirements:
secrets:
- name: DOCKER_TOKEN
provider: docker
- name: DATADOG_API_KEY
provider: datadog
required: false
env:
APP_NAME: my-app
REGISTRY: docker.io/myuser
VERSION: v1.0.0
jobs:
- name: test
steps:
- name: unit-tests
command: npm test
- name: lint
command: npm run lint
- name: build
dependsOn: [test]
steps:
- name: docker-build
script: |
echo "$DOCKER_TOKEN" | docker login -u myuser --password-stdin
docker build -t ${REGISTRY}/${APP_NAME}:${VERSION} .
docker push ${REGISTRY}/${APP_NAME}:${VERSION}
- name: deploy
dependsOn: [build]
env:
NAMESPACE: production
steps:
- name: helm-upgrade
command: |
helm upgrade --install ${APP_NAME} ./chart \
--set image.tag=${VERSION} \
--namespace ${NAMESPACE}
- name: wait-rollout
command: kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE}
- name: notify
dependsOn: [deploy]
steps:
- name: send-notification
script: |
if [ -n "$DATADOG_API_KEY" ]; then
echo "Sending notification to Datadog..."
# Send notification
fi