Skip to content
Docs

Cluster Concepts

Overview

Hyve manages Kubernetes clusters through declarative YAML definitions stored in Git repositories. Each cluster references a module (driver) that implements its lifecycle operations — create, delete, status, and auth. The driver is versioned and locked in hyve.lock, ensuring reproducibility across your team and CI/CD.

Declarative

Define clusters as YAML files in Git

Driver-based

Any cloud provider via the module system

Version Controlled

Full history of every cluster change

Lifecycle Hooks

beforeCreate, onCreate, onDelete, afterDelete

Cluster Definition

Clusters are defined using YAML files stored in the clusters/ directory of your Git state repository. The driver determines which module handles all cloud operations.

apiVersion: v1
kind: Cluster
metadata:
name: my-cluster
region: us-east-1
spec:
driver:
source: github.com/hyve-modules/eks
version: v1.0.0
params:
vpc_id: vpc-0abc123456789
eks_role_arn: arn:aws:iam::123456789012:role/eks-role
node_role_arn: arn:aws:iam::123456789012:role/node-role
instance_type: t3.medium
node_count: "3"
workflows:
beforeCreate:
- provision-network
onCreate:
- deploy-monitoring
onDelete:
- drain-workloads
afterDelete:
- cleanup-network
delete: false
pause: false
expiresAt: ""
pendingWorkflows: []
workflowSchedules: []

Metadata fields

metadata.name string required

Unique cluster identifier. Used as the YAML filename (clusters/<name>.yaml) and injected as HYVE_CLUSTER_NAME into module operations.

metadata.region string required

Cloud provider region (e.g. PHX1, us-east-1, us-central1, eastus). Injected as HYVE_CLUSTER_REGION into module operations.

Driver fields

spec.driver.source string required

Module source string. Examples:

  • github.com/hyve-modules/civo
  • github.com/org/repo//path/to/module
  • ./local-module
spec.driver.version string required

Module version. Must be locked in hyve.lock. Examples: v1.0.0, v2.1.3, local

Params

spec.params object

Module-specific key-value parameters. Each param is injected as HYVE_PARAM_<UPPERCASED_KEY> into module operations. The set of valid params is defined by the module’s module.yaml.

Params are set when creating a template (--set KEY=VALUE) or can be overridden at cluster-creation time.

driverOutputs

spec.driverOutputs object

Key-value pairs emitted by module operations as HYVE_KEY=value stdout lines during reconciliation. These are stored and re-injected on the next reconcile, allowing operations to pass data forward (e.g. cluster endpoint, VPC ID, last param hash).

Do not edit this field manually. It is managed exclusively by the reconciler, and — like spec.appliedResources — physically lives in a separate file from the one you edit. See Reconciler state file below.

Reconciler state file

driverOutputs and appliedResources (see Cluster Resources) are both reconciler-owned: values the reconciler produced, not something a person decides. Keeping them in the same file as spec.driver, spec.params, spec.resources, and the rest of what you actually author would mean every routine reconcile — even one where nothing you declared changed — rewrites that file, mixing machine bookkeeping (content hashes, timestamps, tracked-object lists) into the same diff as your real changes.

Instead, they’re stored in a sidecar file in cluster-state/, a sibling of clusters/ — not next to the cluster’s desired-state YAML, but in its own directory entirely, so ls clusters/ only ever shows files you actually wrote:

clusters/
└── my-cluster.yaml # what you author and review
cluster-state/
└── my-cluster.state.yaml # driverOutputs + appliedResources — reconciler-owned
cluster-state/my-cluster.state.yaml
driverOutputs:
HYVE_EKS_CLUSTER_ENDPOINT: https://abc123.gr7.us-east-1.eks.amazonaws.com
HYVE_LAST_PARAMS_HASH: abc123...
appliedResources:
cert-manager:
sourceSHA256: a1b2c3d4e5f6...
helm: true
namespace: cert-manager
appliedAt: "2026-07-03T10:00:00Z"
objects:
- apiVersion: apps/v1
kind: Deployment
namespace: cert-manager
name: cert-manager

This file is entirely derived from clusters/<name>.yaml plus what the reconciler observes — if it’s ever missing, the reconciler recreates it on the next reconcile with no manual migration step. An older cluster file that still has driverOutputs/appliedResources inline (from before this split existed) keeps working exactly as before; the very next reconcile that touches the cluster splits it into the two-file layout automatically. Likewise, an existing sidecar still sitting directly in clusters/ (from before it moved to its own directory) needs no migration step either — it’s just written back out to cluster-state/ on the next reconcile that touches that cluster.

Lifecycle control

spec.delete boolean default: false

When true, the reconciler runs onDelete workflows, calls the module’s delete operation, runs afterDelete workflows, and removes the YAML file. Set this field (rather than deleting the file directly) to ensure lifecycle hooks run.

spec.pause boolean default: false

When true, the reconciler skips this cluster entirely. The cloud cluster continues running; Hyve does not compare or modify it until pause is set to false.

spec.expiresAt string

RFC 3339 timestamp (e.g. "2026-09-01T00:00:00Z"). When the current time passes this value, the reconciler treats the cluster as if spec.delete: true is set — running lifecycle hooks, deleting from the cloud, and removing the YAML file. A paused cluster is never checked for expiry.

Workflow hooks

spec.workflows.beforeCreate string[]

Workflow names to run before the cluster is provisioned. The cluster does not exist yet — no kubeconfig is available. Use for provisioning prerequisites (VPCs, IAM roles, security groups). Workflow output variables (HYVE_KEY=value) are captured and can populate driverOutputs before create.

spec.workflows.onCreate string[]

Workflow names to run after the cluster is created and the module reports status active. The cluster exists; run hyve cluster auth first if your workflows need kubectl access. Runs before spec.resources applies for this cycle — a workflow here can’t assume any resource-created object (a namespace, a Deployment) already exists.

spec.workflows.afterCreate string[]

Workflow names to run after the cluster is created, authenticated, and — unlike onCreate — after spec.resources has applied for this cycle. Use this when a workflow needs something a resource created (e.g. a namespace) to already exist, such as creating a Kubernetes Secret that a resource-managed Deployment references.

spec.workflows.onDelete string[]

Workflow names to run before the cluster is deleted. Cluster is still running; kubeconfig is available.

spec.workflows.afterDelete string[]

Workflow names to run after the cluster has been deleted. No kubeconfig available. Use for tearing down cloud prerequisites provisioned by beforeCreate workflows.

spec.workflows.preReconcile string[]

Workflow names to run at the start of every reconcile loop for this cluster, before any create/update/delete logic. Runs on every reconcile regardless of whether the cluster definition changed.

Cluster lifecycle

Creation

Execute a template

Terminal window
hyve cluster create my-cluster --template my-civo-template

Generates clusters/my-cluster.yaml and commits it to Git.

Reconcile

Terminal window
hyve reconcile

Runs beforeCreate workflows, calls the module’s create operation, waits for active status, runs onCreate workflows, applies spec.resources, then runs afterCreate workflows.

Authenticate

Terminal window
hyve cluster auth my-cluster
kubectl config use-context my-cluster
kubectl get nodes

Deletion

To delete a cluster and run lifecycle hooks:

Terminal window
# Mark for deletion — runs onDelete, calls module delete, runs afterDelete
hyve cluster delete my-cluster

This sets spec.delete: true in the cluster YAML, commits to Git, and runs reconciliation.

Pausing

Pause a cluster to stop Hyve from touching it during reconciliation (useful for maintenance windows):

Terminal window
# Edit clusters/my-cluster.yaml, set spec.pause: true
# Commit and push — reconciler will skip this cluster

To resume, set spec.pause: false and commit.

Auto-expiry

Set spec.expiresAt to automatically delete a cluster at a future time:

spec:
expiresAt: "2026-09-01T00:00:00Z"

Or use a template with a schedule to compute the expiry automatically at cluster-creation time.

Lifecycle workflow hooks

HookWhenCluster exists?Kubeconfig?
beforeCreateBefore cloud provisioningNoNo
onCreateAfter cluster reaches active status, before spec.resources appliesYesAfter auth
afterCreateAfter cluster creation, after spec.resources has appliedYesAfter auth
onDeleteBefore cloud deprovisioningYesYes
afterDeleteAfter cloud deprovisioningNoNo
preReconcileStart of every reconcile loopDependsDepends

beforeCreate use cases

spec:
workflows:
beforeCreate:
- provision-vpc # Create VPC and subnets for EKS
- create-iam-roles # Create EKS cluster and node IAM roles

These workflows can emit HYVE_KEY=value outputs that are stored in driverOutputs and used by the module’s create operation.

onCreate use cases

spec:
workflows:
onCreate:
- setup-monitoring # Install Prometheus/Grafana
- configure-networking # Apply network policies
- deploy-app # Deploy application workloads

afterCreate use cases

spec:
workflows:
afterCreate:
- create-app-secrets # Create a Secret a resource-managed Deployment references
- point-dns-at-cluster # DNS management once the app is actually deployed

Use afterCreate instead of onCreate whenever a workflow depends on something spec.resources creates — most commonly, a Kubernetes Secret whose real values can’t be committed to Git as a resource, but which a resource-managed Deployment already expects to exist (in its own namespace, also resource-managed). onCreate runs too early for this: the namespace/Deployment don’t exist yet.

onDelete use cases

spec:
workflows:
onDelete:
- backup-data # Final backup before deletion
- drain-workloads # Gracefully drain workloads
- notify-team # Send deletion notification

afterDelete use cases

spec:
workflows:
afterDelete:
- destroy-vpc # Remove VPC and subnets created in beforeCreate
- delete-iam-roles # Clean up IAM roles
- cleanup-dns # Remove DNS records

Reconciliation

Reconciliation reads all cluster YAMLs from Git and calls the appropriate module to bring the cloud state in line with the desired state.

Terminal window
# Reconcile from local machine (local mode)
hyve reconcile
# Reconcile from a checked-out repo path (CI/CD mode)
hyve reconcile --path .

What the reconciler does

Creates missing clusters

If a cluster YAML exists in Git but the cluster does not exist in the cloud, the reconciler calls the module’s create operation and runs beforeCreate → create → onCreate workflows.

Checks existing clusters

For each cluster that exists in both Git and the cloud, the reconciler calls the module’s status operation. If the cluster’s params have changed (detected via HYVE_LAST_PARAMS_HASH), the reconciler may update or recreate the cluster depending on the module.

Deletes marked clusters

If spec.delete: true is set, or if spec.expiresAt has passed, the reconciler runs onDelete workflows, calls the module’s delete operation, and then runs afterDelete workflows before removing the YAML file.

Skips paused clusters

If spec.pause: true is set, the cluster is skipped entirely. No status check, no create, no delete.

Storage

Cluster definitions are stored in:

~/.hyve/repositories/<repository-name>/
├── clusters/
│ ├── my-cluster.yaml # desired state — what you author
│ ├── staging.yaml
│ └── dev.yaml
└── cluster-state/
├── my-cluster.state.yaml # driverOutputs + appliedResources — reconciler-owned
├── staging.state.yaml
└── dev.state.yaml

A cluster with no reconciler-owned state yet (freshly created, nothing applied) has no .state.yaml file — it appears in cluster-state/ on the first save that has something to record. These files are version-controlled in your Git repository. Every change is committed and pushed — providing a full audit trail of all cluster modifications.