This guide covers deploying HyperFleet API to a Kubernetes cluster via Helm chart.
For running the binary directly on your machine (development, debugging), see the Development Guide.
Before deploying, ensure you have:
- Kubernetes cluster (1.25+)
- Helm 3 CLI
- PostgreSQL database — either:
- An external managed instance (Cloud SQL, RDS, Azure Database) for production, or
- The chart's built-in PostgreSQL pod for evaluation and testing
- Container image — a released hyperfleet-api image, a pre-built image from your registry, or build your own:
make image \ IMAGE_REGISTRY=quay.io/yourorg \ IMAGE_TAG=v1.0.0 podman push quay.io/yourorg/hyperfleet-api:v1.0.0
The fastest path to a running deployment. This uses the chart's built-in PostgreSQL and no authentication — suitable for evaluation and testing.
Three values are required (they have no usable defaults):
| Value | What to set | Example |
|---|---|---|
image.registry |
Container registry domain | quay.io |
image.repository |
Organization and image name | openshift-hyperfleet/hyperfleet-api |
image.tag |
Image version | v1.0.0 |
Deploy:
helm install hyperfleet-api ./charts/ \
--namespace hyperfleet-system \
--create-namespace \
--set image.registry=quay.io \
--set image.repository=openshift-hyperfleet/hyperfleet-api \
--set image.tag=v1.0.0Verify:
kubectl get pods --namespace hyperfleet-system
kubectl port-forward svc/hyperfleet-api 8000:8000 --namespace hyperfleet-system
curl http://localhost:8000/api/hyperfleet/v1/clustersThis creates a HyperFleet API deployment, a PostgreSQL StatefulSet, and the necessary Services, ConfigMaps, and Secrets.
For production, use an external managed database and store credentials in a Kubernetes Secret.
kubectl create secret generic hyperfleet-db-external \
--namespace hyperfleet-system \
--from-literal=db.host=<your-db-host> \
--from-literal=db.port=5432 \
--from-literal=db.name=hyperfleet \
--from-literal=db.user=hyperfleet \
--from-literal=db.password=<your-password>helm install hyperfleet-api ./charts/ \
--namespace hyperfleet-system \
--create-namespace \
--set image.registry=quay.io \
--set image.repository=openshift-hyperfleet/hyperfleet-api \
--set image.tag=v1.0.0 \
--set database.postgresql.enabled=false \
--set database.external.enabled=true \
--set database.external.secretName=hyperfleet-db-externalThe chart injects database credentials as environment variables using secretKeyRef — credentials are never exposed in ConfigMaps or pod specs.
How configuration flows in Kubernetes (click to expand)
┌─────────────────────────────────────────────────────────────┐
│ Helm Chart │
│ │
│ values.yaml │
│ ├─ server.port, logging.level, etc. │
│ └─ database.external.secretName │
└──────────────────┬──────────────────────────────────────────┘
│
├─────────────────┬────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌─────────────┐ ┌───────────────┐
│ ConfigMap │ │ Secret │ │ Deployment │
│ │ │ │ │ │
│ Non-sensitive: │ │ Sensitive: │ │ Env vars: │
│ - server.host │ │ - db.host │ │ - HYPERFLEET │
│ - server.port │ │ - db.user │ │ _CONFIG │
│ - logging.level │ │ - db.pass │ │ - secretKeyRef│
└──────┬───────────┘ └──────┬──────┘ └───────┬───────┘
│ │ │
└────────────────────┴────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Pod │
│ │
│ Volume Mounts: │
│ - /etc/hyperfleet/config.yaml │
│ (from ConfigMap) │
│ │
│ Environment Variables: │
│ - HYPERFLEET_CONFIG= │
│ /etc/hyperfleet/config.yaml │
│ - HYPERFLEET_DATABASE_HOST= │
│ (from Secret via secretKeyRef) │
│ - HYPERFLEET_DATABASE_PASSWORD= │
│ (from Secret via secretKeyRef) │
└─────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Application │
│ │
│ 1. Load config from file │
│ (/etc/hyperfleet/config.yaml) │
│ 2. Apply environment variables │
│ 3. Apply CLI flags (if any) │
│ │
│ Priority: Flags > Env Vars > │
│ ConfigMap > Defaults │
└─────────────────────────────────────┘
JWT authentication is disabled by default in the Helm chart. To enable it, set the config.server.jwt.* properties, like so:
helm install hyperfleet-api ./charts/ \
--namespace hyperfleet-system \
--set image.registry=quay.io \
--set image.repository=openshift-hyperfleet/hyperfleet-api \
--set image.tag=v1.0.0 \
--set config.server.jwt.enabled=true \
--set config.server.jwt.issuer_url=https://your-idp.example.com/auth/realms/your-realm \
--set config.server.jwk.cert_url=https://your-idp.example.com/auth/realms/your-realm/protocol/openid-connect/certs| Value | Required when JWT enabled | Description |
|---|---|---|
config.server.jwt.enabled |
Yes | Set to true |
config.server.jwt.issuer_url |
Yes | Expected JWT issuer URL for token validation |
config.server.jwk.cert_url |
Yes (unless cert_file is set) |
URL to fetch JWK signing keys |
config.server.jwt.audience |
No | Expected JWT audience claim |
config.server.jwt.identity_claim |
No | JWT claim used as caller identity (default: email) |
See Authentication for full reference including identity header configuration and caller identity details.
Adapters are external components (validation, DNS, pull-secret, HyperShift) that report status back to HyperFleet API. The required adapter lists define which adapters must report "ready" before a resource is considered Reconciled.
By default, no adapters are required ([]). For production, configure the adapters your deployment uses:
--set 'config.adapters.required.cluster={validation,dns,pullsecret,hypershift}' \
--set 'config.adapters.required.nodepool={validation,hypershift}'Or in a values file:
config:
adapters:
required:
cluster:
- validation
- dns
- pullsecret
- hypershift
nodepool:
- validation
- hypershiftThe API can validate cluster and nodepool spec fields against a custom OpenAPI schema on every create/update request. This is disabled by default.
Provide the schema content directly in your values file:
validationSchema:
enabled: true
content: |
openapi: 3.0.0
info:
title: My Validation Schema
version: 1.0.0
paths: {}
components:
schemas:
ClusterSpec:
type: object
required: [region]
properties:
region:
type: string
NodePoolSpec:
type: object
required: [machine_type]
properties:
machine_type:
type: stringReference a ConfigMap that already exists in the namespace (must contain an openapi.yaml key):
validationSchema:
enabled: true
existingConfigMap: my-validation-schemaWhen enabled, the chart creates (or references) a ConfigMap with the schema, mounts it into the container, and configures the API to validate against it. The API will fail to start if the schema is invalid.
helm upgrade hyperfleet-api ./charts/ \
--namespace hyperfleet-system \
--set image.tag=v1.1.0During upgrade, in case schema changes have occurred in the new version, a DB migration will be handled automatically. See Migration.
helm uninstall hyperfleet-api --namespace hyperfleet-systemFor repeatable deployments, create a values.yaml file:
image:
registry: quay.io
repository: openshift-hyperfleet/hyperfleet-api
tag: v1.0.0
config:
server:
jwt:
enabled: true
issuer_url: https://your-idp.example.com/auth/realms/your-realm
jwk:
cert_url: https://your-idp.example.com/auth/realms/your-realm/protocol/openid-connect/certs
adapters:
required:
cluster:
- validation
- dns
- pullsecret
- hypershift
nodepool:
- validation
- hypershift
database:
postgresql:
enabled: false
external:
enabled: true
secretName: hyperfleet-db-external
replicaCount: 3
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mihelm install hyperfleet-api ./charts/ \
--namespace hyperfleet-system \
--values values.yaml| Parameter | Description | Default |
|---|---|---|
image.registry |
Container registry | CHANGE_ME (must be set) |
image.repository |
Image repository | CHANGE_ME (must be set) |
image.tag |
Image tag | "" (must be set) |
image.pullPolicy |
Image pull policy | Always |
config.server.jwt.enabled |
Enable JWT authentication | false |
config.adapters.required.cluster |
Cluster adapters required for Reconciled state | [] |
config.adapters.required.nodepool |
Nodepool adapters required for Reconciled state | [] |
database.postgresql.enabled |
Enable built-in PostgreSQL | true |
database.external.enabled |
Use external database | false |
database.external.secretName |
Secret containing database credentials | "" |
validationSchema.enabled |
Enable spec validation schema | false |
replicaCount |
Number of API replicas | 1 |
resources.limits.cpu |
CPU limit | 500m |
resources.limits.memory |
Memory limit | 512Mi |
podDisruptionBudget.enabled |
Enable PodDisruptionBudget | false |
podDisruptionBudget.minAvailable |
Minimum available pods during disruption | 1 |
serviceMonitor.enabled |
Enable Prometheus Operator ServiceMonitor | false |
serviceMonitor.interval |
Metrics scrape interval | 30s |
serviceMonitor.scrapeTimeout |
Metrics scrape timeout | 10s |
serviceMonitor.labels |
Additional labels for Prometheus selector | {} |
serviceMonitor.namespace |
Namespace for ServiceMonitor (if different) | "" |
See Configuration Guide for the complete application configuration reference and charts/values.yaml for all Helm-specific settings.
helm status hyperfleet-api --namespace hyperfleet-system
helm list --namespace hyperfleet-system
kubectl get pods --namespace hyperfleet-system
kubectl get svc --namespace hyperfleet-systemkubectl logs -f deployment/hyperfleet-api --namespace hyperfleet-system
kubectl logs -f -l app=hyperfleet-api --namespace hyperfleet-system
# PostgreSQL logs (if using built-in)
kubectl logs -f statefulset/hyperfleet-postgresql --namespace hyperfleet-systemkubectl describe pod <pod-name> --namespace hyperfleet-system
kubectl get events --namespace hyperfleet-system --sort-by='.lastTimestamp'
kubectl exec -it deployment/hyperfleet-api --namespace hyperfleet-system -- /bin/sh
kubectl get secrets --namespace hyperfleet-system
kubectl get configmaps --namespace hyperfleet-systemThe deployment includes:
- Liveness probe:
GET /healthz(port 8080) — returns 200 if the process is alive - Readiness probe:
GET /readyz(port 8080) — returns 200 when ready to receive traffic, 503 during startup/shutdown - Metrics:
GET /metrics(port 9090) — Prometheus metrics endpoint
# Manual scaling
kubectl scale deployment hyperfleet-api --replicas=3 --namespace hyperfleet-system
# Via Helm
helm upgrade hyperfleet-api ./charts/ \
--namespace hyperfleet-system \
--set replicaCount=3Enable autoscaling via Helm values (autoscaling.enabled=true).
Prometheus metrics are available at http://<service>:9090/metrics.
# Enable ServiceMonitor
helm install hyperfleet-api ./charts/ \
--namespace hyperfleet-system \
--set image.registry=quay.io \
--set image.repository=openshift-hyperfleet/hyperfleet-api \
--set image.tag=v1.0.0 \
--set serviceMonitor.enabled=true
# With custom Prometheus selector labels
--set serviceMonitor.labels.release=prometheus
# ServiceMonitor in a different namespace
--set serviceMonitor.namespace=monitoringBefore deploying to production, ensure:
- Image: Specific version tag set (not
latestor empty) - Database: External managed database configured (Cloud SQL, RDS, Azure Database)
- Secrets: Database credentials stored in a Secret (not ConfigMap)
- Authentication: JWT enabled with issuer and JWK URL configured
- Adapters: Required adapters specified for cluster and nodepool
- Config file permissions: Config files (
--config/HYPERFLEET_CONFIG) must be operator-trusted — see below - Resources: CPU/memory limits and requests set
- Replicas: Multiple replicas configured (
replicaCount >= 2) - Disruption: PodDisruptionBudget enabled (
podDisruptionBudget.enabled=true) - Monitoring: ServiceMonitor enabled if using Prometheus Operator
The configuration file path — set via --config or HYPERFLEET_CONFIG — is a trust boundary. The API validates configuration content on startup (unknown fields are rejected, required values are enforced, TLS/JWT/timeout settings are checked) and will refuse to start with an invalid configuration. However, path and permission safety is the operator's responsibility. The API reads whatever file the process can access at the given path without checking permissions or ownership.
Ensure configuration files are:
- Owned by the service account running the API (e.g.,
root:rootor a dedicated user) - Mode
0600(owner read/write only) or0640if group-readable access is needed - Never world-writable
In Helm deployments, the chart mounts the configuration as a ConfigMap volume at /etc/hyperfleet/config.yaml with default Kubernetes permissions, which satisfies these requirements. This guidance applies primarily to bare-metal or VM deployments where config files are managed directly on disk.
# 1. Build and push image
export QUAY_USER=myuser
podman login quay.io
make image-dev
# 2. Get GKE credentials
gcloud container clusters get-credentials my-cluster \
--zone=us-central1-a \
--project=my-project
# 3. Create namespace
kubectl create namespace hyperfleet-system
kubectl config set-context --current --namespace=hyperfleet-system
# 4. Create database secret
kubectl create secret generic hyperfleet-db-external \
--from-literal=db.host=10.10.10.10 \
--from-literal=db.port=5432 \
--from-literal=db.name=hyperfleet \
--from-literal=db.user=hyperfleet \
--from-literal=db.password=secretpassword
# 5. Deploy with Helm
helm install hyperfleet-api ./charts/ \
--set image.registry=quay.io \
--set image.repository=myuser/hyperfleet-api \
--set image.tag=dev-abc123 \
--set config.server.jwt.enabled=false \
--set database.postgresql.enabled=false \
--set database.external.enabled=true \
--set database.external.secretName=hyperfleet-db-external \
--set 'config.adapters.required.cluster={validation,dns,pullsecret,hypershift}' \
--set 'config.adapters.required.nodepool={validation,hypershift}'
# 6. Verify deployment
kubectl get pods
kubectl logs -f deployment/hyperfleet-api
# 7. Access API (port-forward for testing)
kubectl port-forward svc/hyperfleet-api 8000:8000
curl http://localhost:8000/api/hyperfleet/v1/clusters- Configuration Guide — Complete configuration reference
- Authentication — Authentication configuration
- Development Guide — Local execution, development setup, and workflows