Skip to Content
AppendicesFuzzballOrchestrate CRD Reference

FuzzballOrchestrate CRD Reference

The FuzzballOrchestrate Custom Resource Definition (CRD) is the primary way to deploy and configure a Fuzzball cluster on Kubernetes. This CRD provides comprehensive control over all aspects of a Fuzzball deployment, from basic infrastructure (database, ingress, authentication) to advanced features (multi-cloud provisioning, billing integration, autoscaling).

Basic Example

The minimal configuration requires image credentials, ingress configuration, database setup, and authentication. This example is suitable for local or on-premises deployments:

apiVersion: deployment.ciq.com/v1alpha1 kind: FuzzballOrchestrate metadata: name: fuzzball-orchestrate spec: image: username: ${DEPOT_USER} password: ${ACCESS_KEY} exclusive: false ingress: create: domain: "10.0.0.99.nip.io" proxy: type: LoadBalancer annotations: metallb.io/loadBalancerIPs: 10.0.0.99 database: create: storage: class: longhorn keycloak: create: ingress: hostname: auth.10.0.0.99.nip.io realmName: Fuzzball realmId: 550e8400-e29b-41d4-a716-446655440000 username: keycloak ownerEmail: "admin@example.com" createDatabase: true tls: certManager: create: {} trustManager: create: {} fuzzball: substrate: nfs: destination: "/fuzzball/shared" server: 10.0.0.10 path: "/srv/fuzzball/shared" jetstream: replicas: 3 storage: class: longhorn size: 10Gi

Configuration Sections

Note

Many credential fields (passwords, API keys, TLS certificates, image pull secrets) support references to Kubernetes Secrets as an alternative to inline values. Secret references are recommended for production deployments. See Kubernetes Secret References for the full list of fields and usage examples.

Image Configuration

Controls where container images are pulled from and authentication:

ParameterTypeRequiredDefaultDescription
repositorystringNodepot.ciq.com/fuzzball/fuzzball-imagesContainer image registry
credentialsSecretNamestringNo-Name of a kubernetes.io/dockerconfigjson Secret with registry credentials (recommended)
usernamestringNo-Registry authentication username (deprecated — use credentialsSecretName)
passwordstringNo-Registry authentication password (deprecated — use credentialsSecretName)
exclusivebooleanNotrueIf true, all images must come from specified repository

Example with inline credentials:

spec: image: repository: depot.ciq.com/fuzzball/fuzzball-images username: my-depot-user password: my-depot-token exclusive: false

Example with Secret reference (recommended for production):

spec: image: credentialsSecretName: depot-pull-secret exclusive: false

Ingress Configuration

Defines how the cluster is exposed to the network. Choose either create (new ingress) or external (existing ingress controller).

Option A: Create New Ingress

ParameterTypeRequiredDefaultDescription
domainstringYes-Base domain for all Fuzzball services
proxy.typestringYesLoadBalancerService type: LoadBalancer or NodePort
proxy.annotationsmapNo{}Annotations for the proxy service (e.g., MetalLB config)
proxy.http.nodePortintegerNo-NodePort for HTTP (if type is NodePort)
proxy.tls.nodePortintegerNo-NodePort for HTTPS (if type is NodePort)

Example with LoadBalancer:

spec: ingress: create: domain: fuzzball.example.com proxy: type: LoadBalancer annotations: metallb.io/loadBalancerIPs: 10.0.0.99 metallb.io/allow-shared-ip: ingress-and-fuzzball
CoreWeave LoadBalancer Annotations

CoreWeave requires specific annotations on the LoadBalancer service for automatic DNS configuration:

AnnotationValueDescription
service.beta.kubernetes.io/coreweave-load-balancer-typepublicCreates an internet-accessible load balancer
service.beta.kubernetes.io/external-hostname*.<domain>Wildcard domain for automatic DNS resolution

CoreWeave example:

spec: ingress: create: domain: a1b2c3-my-cluster.coreweave.app proxy: type: LoadBalancer annotations: service.beta.kubernetes.io/coreweave-load-balancer-type: public service.beta.kubernetes.io/external-hostname: '*.a1b2c3-my-cluster.coreweave.app'

Example with NodePort:

spec: ingress: create: domain: fuzzball.example.com proxy: type: NodePort http: nodePort: 30080 tls: nodePort: 30443

Option B: Use External Ingress

ParameterTypeRequiredDefaultDescription
domainstringYes-Base domain for all Fuzzball services
classNamestringYes-Ingress class name (e.g., nginx, traefik)
annotationsmapNo{}Annotations for ingress resources

Example:

spec: ingress: external: domain: fuzzball.example.com className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod

Database Configuration

Fuzzball requires a PostgreSQL database. Choose either create (deploys a dedicated PostgreSQL alongside Orchestrate) or external.

Option A: Create Dedicated Database

ParameterTypeRequiredDefaultDescription
enableDebugPodbooleanNofalseDeploy a debug pod with database tools
annotationsmapNo{}Annotations for database resources
storage.classstringNo-StorageClass for database PVC
storage.sizestringNo-Size of database storage
storage.accessModestringNoReadWriteOnceAccess mode for PVC
storage.annotationsmapNo{}Annotations for PVC

Example:

spec: database: create: enableDebugPod: true storage: class: longhorn size: 200Gi accessMode: ReadWriteOnce

Option B: Use External Database

ParameterTypeRequiredDefaultDescription
hoststringYes-Database hostname or IP
portstringNo5432Database port
driverstringNopostgresDatabase driver
credentials.userstringYes-Database username
credentials.passwordstringNo-Database password (inline)
credentials.passwordRefobjectNo-Reference to a K8s Secret containing the database password (preferred over inline)
sslModestringNoverify-fullSSL mode: disable, require, verify-ca, verify-full
rdsSecretIdstringNo-AWS Secrets Manager ARN for RDS credentials
certificate.caCertstringNo-CA certificate for SSL verification (inline)
certificate.caCertURLstringNo-URL to download CA certificate
certificate.caCertRefobjectNo-Reference to a K8s Secret containing the CA certificate (preferred over inline)
certificate.clientCertstringNo-Client certificate for mTLS (inline)
certificate.clientCertRefobjectNo-Reference to a K8s Secret containing the client certificate (preferred over inline)
certificate.clientKeystringNo-Client key for mTLS (inline)
certificate.clientKeyRefobjectNo-Reference to a K8s Secret containing the client key (preferred over inline)

Example with external PostgreSQL:

spec: database: external: host: postgres.example.com port: "5432" driver: postgres credentials: user: fuzzball passwordRef: name: db-credentials key: password sslMode: verify-full

Example with AWS RDS:

spec: database: external: host: mydb.abc123.us-east-1.rds.amazonaws.com rdsSecretId: arn:aws:secretsmanager:us-east-1:123456789012:secret:rds-db-credentials-abc123 sslMode: verify-full certificate: caCertURL: https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem

Keycloak Authentication

For Keycloak-backed deployments, Fuzzball delegates authentication to Keycloak. Choose either create (deploy Keycloak alongside Orchestrate) or external. As of v3.3.0, Keycloak is optional — deployments that use the local-organization flow authenticate directly with Fuzzball without a Keycloak instance.

Option A: Deploy Keycloak

Note

As of v3.3.0, plaintext password and defaultUserPassword fields are deprecated. Use the passwordRef and defaultUserPasswordRef fields to reference passwords stored in Kubernetes Secrets. The plaintext fields continue to work but will be removed in a future release.

ParameterTypeRequiredDefaultDescription
ownerEmailstringYes-Email address for the Fuzzball organization owner
realmIdstringNo(generated)Keycloak realm ID (must be UUID v4, lower case)
realmNamestringNoFuzzballKeycloak realm display name
usernamestringNokeycloakKeycloak admin username
passwordstringNo(generated)Deprecated. Keycloak admin password (plaintext). Use passwordRef instead.
passwordRef.namestringNo-Name of Kubernetes Secret containing the Keycloak admin password
passwordRef.keystringNo-Key within the Secret for the Keycloak admin password
passwordRef.namespacestringNofuzzball-systemNamespace of the Secret; defaults to the Fuzzball system namespace
defaultUserPasswordstringNo-Deprecated. Default password for new users (plaintext). Use defaultUserPasswordRef instead.
defaultUserPasswordRef.namestringNo-Name of Kubernetes Secret containing the default user password
defaultUserPasswordRef.keystringNo-Key within the Secret for the default user password
defaultUserPasswordRef.namespacestringNofuzzball-systemNamespace of the Secret; defaults to the Fuzzball system namespace
createDatabasebooleanNotrueCreate dedicated database for Keycloak
replicasintegerNo1Number of Keycloak replicas
ingress.hostnamestringNoauth.{domain}Hostname for Keycloak UI
ingress.classNamestringNo-Ingress class name
ingress.tls.certstringNo-TLS certificate (inline)
ingress.tls.keystringNo-TLS private key (inline)
ingress.tls.secretNamestringNo-Name of a kubernetes.io/tls Secret containing the TLS cert and key (preferred over inline)
ingress.annotationsmapNo{}Ingress annotations

Preferred example using SecretKeyReference:

spec: keycloak: create: ownerEmail: admin@example.com realmName: MyOrganization realmId: 550e8400-e29b-41d4-a716-446655440000 username: keycloak passwordRef: name: keycloak-credentials key: admin-password defaultUserPasswordRef: name: keycloak-credentials key: default-user-password createDatabase: true ingress: hostname: auth.fuzzball.example.com

Initial Setup

Generating a Realm ID

Generate a UUID v4 for the realm ID:

$ uuidgen

Use the output in your configuration:

realmId: 550e8400-e29b-41d4-a716-446655440000
Setting the Owner Account

The ownerEmail and defaultUserPassword fields create the initial administrator account with full cluster administration privileges. Change the default password after first login.

ownerEmail: admin@example.com defaultUserPassword: initial-secure-password

LDAP Integration

Keycloak can be configured to federate users from an LDAP/Active Directory server:

ParameterTypeRequiredDescription
ldap.urlstringYesLDAP server URL (ldap:// or ldaps://)
ldap.startTLSbooleanNoUse StartTLS for ldap:// connections
ldap.insecurebooleanNoSkip TLS certificate verification
ldap.vendorstringNoLDAP vendor: ad, rhds, tivoli, edirectory, other
ldap.bindDNstringNoBind DN for LDAP authentication
ldap.bindPasswordstringNoPassword for bind DN (inline)
ldap.bindPasswordRefobjectNoReference to a K8s Secret containing the bind password (preferred over inline)
ldap.searchScopestringNoSearch scope: single or subtree
ldap.users.dnstringYesBase DN for user search
ldap.users.attributes.*stringYesUser attribute mappings
ldap.users.objectClasses[]stringYesUser object classes
ldap.users.filterstringNoLDAP filter for users
ldap.groups.dnstringYesBase DN for group search
ldap.groups.membershipAttributeTypestringYesMembership type: dn or uid
ldap.groups.userGroupsStrategystringYesStrategy for loading groups
ldap.groups.attributes.*stringYesGroup attribute mappings
ldap.groups.objectClasses[]stringYesGroup object classes
ldap.groups.filterstringNoLDAP filter for groups

LDAP example:

spec: keycloak: create: ownerEmail: admin@example.com ldap: url: ldaps://ldap.example.com vendor: ad bindDN: cn=fuzzball,ou=service,dc=example,dc=com bindPassword: ldap-bind-password searchScope: subtree users: dn: ou=users,dc=example,dc=com attributes: username: sAMAccountName rdn: sAMAccountName uuid: objectGUID uidNumber: uidNumber gidNumber: gidNumber objectClasses: - person - organizationalPerson filter: "(memberOf=cn=fuzzball-users,ou=groups,dc=example,dc=com)" groups: dn: ou=groups,dc=example,dc=com membershipAttributeType: dn userGroupsStrategy: get_groups_from_user_memberof_attribute attributes: groupName: cn groupMembership: member userMembership: sAMAccountName memberOf: memberOf gidNumber: gidNumber objectClasses: - group filter: "(cn=fuzzball-*)"

Option B: Use External Keycloak

ParameterTypeRequiredDescription
urlstringYesKeycloak server URL
realmIdstringYesExisting realm ID (UUID v4)
realmNamestringYesExisting realm name
usernamestringYesAdmin username
passwordstringNoAdmin password (inline)
passwordRefobjectNoReference to a K8s Secret containing the admin password (preferred over inline)
ownerEmailstringYesOrganization owner email
useLDAPFederationbooleanNoWhether LDAP is configured

Example:

spec: keycloak: external: url: https://keycloak.example.com realmId: 550e8400-e29b-41d4-a716-446655440000 realmName: ExistingRealm username: admin passwordRef: name: keycloak-credentials key: admin-password ownerEmail: admin@example.com

TLS Configuration

Optional configuration for certificate management and Let's Encrypt:

cert-manager Deployment

ParameterTypeRequiredDescription
certManager.createobjectNoDeploy cert-manager (empty object for defaults)
certManager.serviceAccount.annotationsmapNoService account annotations (for IRSA)
trustManager.createobjectNoDeploy trust-manager (empty object for defaults)
Default behavior (empty objects for certManager and trustManager) is to issue self-signed certificates

Certificate Issuers

ParameterTypeRequiredDescription
internalIssuerobjectNoInternal certificate issuer configuration
ingressIssuer.create.letsEncrypt.emailstringNoEmail for Let's Encrypt notifications
ingressIssuer.create.letsEncrypt.issuerstringNoIssuer name (e.g., letsencrypt-prod, letsencrypt-staging)
ingressIssuer.create.letsEncrypt.solvers[]objectNoACME challenge solvers (dns01 or http01)
ingressIssuer.external.issuerNamestringNoName of existing ClusterIssuer for ingress TLS (preferred)
ingressIssuer.external.internalCAIssuerNamestringNoName of existing ClusterIssuer for ingress TLS (deprecated, use issuerName)
internalIssuer.external.issuerNamestringNoName of existing ClusterIssuer for internal TLS (preferred)
internalIssuer.external.internalCAIssuerNamestringNoName of existing ClusterIssuer for internal TLS (deprecated, use issuerName)
internalIssuer.create.customCA.inline.secretNamestringNoReference to an existing kubernetes.io/tls Secret providing the CA cert/key for the internal issuer (otherwise the operator generates a self-signed CA)
ingressIssuer.create.customCA.inline.secretNamestringNoReference to an existing kubernetes.io/tls Secret providing the CA cert/key for the ingress issuer
Note

When using external issuers, the operator can automatically extract the CA certificate from the ClusterIssuer and distribute it via trust-manager, assuming trust-manager is running. Enable trustManager.create so the operator deploys it, or install trust-manager separately. When trust-manager is available, manual creation of the trusted-ca-certs-bundle ConfigMap is no longer required. trustManager.create can be set independently of certManager.create when using external issuers.

Example with Let's Encrypt (DNS-01):

spec: tls: certManager: create: {} trustManager: create: {} ingressIssuer: create: letsEncrypt: email: admin@example.com issuer: letsencrypt-prod solvers: - dns01: route53: region: us-east-1 hostedZoneID: Z1234567890ABC

Example with Let's Encrypt (HTTP-01):

spec: tls: certManager: create: {} ingressIssuer: create: letsEncrypt: email: admin@example.com issuer: letsencrypt-prod solvers: - http01: ingress: class: nginx

Fuzzball Services Configuration

The fuzzball section controls all Fuzzball services and their configurations.

Global Settings

ParameterTypeDescription
versionstringFuzzball version (defaults to operator version)
cluster.namestringCluster name (default: unset-cluster)
cluster.kindstringCluster kind/type

Substrate Configuration

Controls how substrate nodes connect and operate:

ParameterTypeDescription
substrate.nfs.destinationstringMount point on substrate nodes
substrate.nfs.serverstringNFS server IP or hostname
substrate.nfs.pathstringNFS export path
substrate.secureRegistries[]stringPrivate registries requiring authentication
substrate.imageProxystringHTTP(S) proxy for image pulling
substrate.imageNoProxy[]stringHosts to exclude from proxy
substrate.mtls.*objectmTLS configuration for substrate

Example:

spec: fuzzball: cluster: name: my-fuzzball-cluster kind: on-premises substrate: nfs: destination: /fuzzball/shared server: 10.0.0.10 path: /srv/fuzzball/shared secureRegistries: - depot.ciq.com imageProxy: http://proxy.example.com:3128 imageNoProxy: - localhost - 127.0.0.1 - .example.com

Kubernetes Scheduling Configuration

Three scheduling fields at the top level of the CRD spec (spec.nodeSelector, spec.tolerations, spec.affinity) apply to every Fuzzball service pod as global defaults:

ParameterTypeDescription
nodeSelectormapLabel selector: only schedule pods onto nodes with matching labels
tolerations[]objectAllow pods to schedule onto nodes with matching taints
affinityobjectNode and pod affinity/anti-affinity rules (standard Kubernetes affinity object)

Each individual service spec supports the same three fields as per-component overrides. When set on a service, the override replaces (does not merge with) the global value for that service.

Example — place all services on a dedicated node pool, but keep Jetstream on storage-optimized nodes:

spec: nodeSelector: node-pool: fuzzball-system # global default tolerations: - key: fuzzball operator: Equal value: system effect: NoSchedule fuzzball: jetstream: nodeSelector: node-pool: storage-optimized # overrides global for Jetstream only tolerations: - key: storage operator: Exists effect: NoSchedule

Example — node affinity for a specific availability zone:

spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - us-east-1a

See the Kubernetes documentation for full syntax details on tolerations and affinity objects.

Service-Specific Configuration

Most Fuzzball services support the following common parameters:

ParameterTypeDescription
replicasintegerNumber of pod replicas
autoscaling.enabledbooleanEnable horizontal pod autoscaling
autoscaling.minReplicasintegerMinimum replicas when autoscaling
autoscaling.maxReplicasintegerMaximum replicas when autoscaling
autoscaling.targetCPUUtilizationintegerTarget CPU percentage (default: 80)
autoscaling.targetMemoryUtilizationintegerTarget memory percentage (default: 80)
resources.requestsmapResource requests (cpu, memory) as key-value pairs
resources.limitsmapResource limits (cpu, memory) as key-value pairs
serviceAccount.annotationsmapService account annotations (for IRSA)
nodeSelectormapOverride global nodeSelector for this service only
tolerations[]objectOverride global tolerations for this service only
affinityobjectOverride global affinity for this service only

Example for orchestrator service:

spec: fuzzball: orchestrator: replicas: 2 autoscaling: enabled: true minReplicas: 1 maxReplicas: 5 targetCPUUtilization: 70 targetMemoryUtilization: 75 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2000m memory: 2Gi serviceAccount: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/fuzzball-orchestrator

Available Configuration Sections

The following configuration sections are available under spec.fuzzball:

Top-Level Configuration:

  • version - Fuzzball version (defaults to operator version)
  • config - Global configuration (shared storage, etc.)
  • cluster - Cluster name and metadata
  • substrate - Substrate node configuration (NFS, registries, proxies, mTLS)

Active Services:

  • agent - Agent service (workflow execution)
  • audit - Audit logging service
  • auth - Authentication service (SpiceDB)
  • billing - Billing and marketplace integration
  • clusterAdmin - Admin UI and cluster setup
  • jetstream - NATS Jetstream message broker
  • openapi - OpenAPI documentation service
  • orchestrator - Orchestrator service
  • storage - Storage service
  • substrateBridge - Substrate bridge service (DNS, logging, Kubernetes integration)
  • ui - Web UI service
  • workflow - Workflow service
  • workflowCatalog - Workflow catalog configuration

The following services have been deprecated as of version v3.0 and will be removed in a future version (use recommended replacements):

  • account - Deprecated (use agent)
  • dns - Deprecated (use substrateBridge)
  • kube - Deprecated (use substrateBridge)
  • log - Deprecated (use substrateBridge)
  • organization - Deprecated (use agent)
  • provision - Deprecated (use orchestrator)
  • schedule - Deprecated (use orchestrator)
  • secret - Deprecated (use agent)
  • user - Deprecated (use agent)
  • workflowEngine - Deprecated (use jetstream)

Jetstream Configuration

NATS Jetstream has specific configuration requirements:

ParameterTypeDefaultDescription
replicasinteger3Number of Jetstream replicas (recommended: 3)
storage.classstring-StorageClass for Jetstream PVCs
storage.sizestring10GiStorage size per replica
externalService.typestring-External service type (NodePort, LoadBalancer)

Example:

spec: fuzzball: jetstream: replicas: 3 storage: class: longhorn size: 20Gi externalService: type: NodePort

Storage Service Configuration

Note

This storage is necessary for internal services. Admins can configure storage for computational jobs later.

ParameterTypeDescription
storage.storage.classstringStorageClass for storage service

Example:

spec: fuzzball: storage: storage: class: longhorn

Audit Service Configuration

ParameterTypeDescription
audit.storage.classstringStorageClass for audit logs

Example:

spec: fuzzball: audit: storage: class: longhorn

Substrate Bridge Configuration

ParameterTypeDescription
substrateBridge.log.storage.classstringStorageClass for substrate logs
substrateBridge.dns.externalService.typestringDNS service type
Note

NodePort is recommended for local deployments.

Example:

spec: fuzzball: substrateBridge: log: storage: class: longhorn dns: externalService: type: NodePort

Orchestrator Provisioner Configuration

The orchestrator provisioner enables multi-cloud and HPC integration:

ParameterTypeDescription
enabledbooleanEnable provisioner
substrateComputeDirectorystringWorking directory for substrate operations
awsobjectAWS provisioner configuration
coreweaveobjectCoreWeave provisioner configuration
slurmobjectSlurm provisioner configuration
pbsobjectPBS provisioner configuration

AWS Provisioner

ParameterTypeDescription
aws.enabledbooleanEnable AWS provisioner
aws.regionstringAWS region
aws.subnetIDs[]stringVPC subnet IDs
aws.securityGroupIDs[]stringSecurity group IDs
aws.instanceProfileARNstringIAM instance profile ARN
aws.usePublicIPbooleanAssign public IPs
aws.sshEnabledbooleanEnable SSH access
aws.sshKeyPairNamestringEC2 key pair name
aws.sshPrivateKeyPemstringSSH private key
aws.depotUserstringCIQ Depot username for substrate nodes
aws.depotAccessTokenstringCIQ Depot access token for substrate nodes
aws.cloudInitScripts[]stringCustom cloud-init scripts to run on instances
aws.sharedFsmapShared filesystem configuration

Example:

spec: fuzzball: orchestrator: provisioner: enabled: true aws: enabled: true region: us-east-1 subnetIDs: - subnet-0123456789abcdef0 securityGroupIDs: - sg-0123456789abcdef0 instanceProfileARN: arn:aws:iam::123456789012:instance-profile/FuzzballSubstrate usePublicIP: false

CoreWeave Provisioner

The CoreWeave provisioner enables dynamic provisioning of compute nodes on CoreWeave infrastructure. When enabled, Fuzzball will automatically create and manage CoreWeave NodePool resources and deploy substrate pods as DaemonSets on provisioned nodes.

CoreWeave deployments use two types of shared storage PVCs:

  1. Workflow Data PVC (fuzzball-shared-storage): Mounted at /mnt/shared-storage on substrate nodes and exposed to workflows via the HostPath CSI driver
  2. Image Cache PVC (fuzzball-sharedfs): Mounted at /mnt/fuzzball-sharedfs on substrate nodes for internal container image caching (not exposed to workflows)
Warning

CoreWeave's shared-vast storage requires Native Protocol Limit view policy. Ensure this is configured before deployment.

Workflow Data Storage Parameters
ParameterTypeRequiredDefaultDescription
coreweave.enabledbooleanYes-Enable CoreWeave provisioner
coreweave.storage.classstringNo-StorageClass for CoreWeave shared storage PVCs (typically shared-vast)
coreweave.storage.sizestringNo-Size of shared storage per substrate node
coreweave.storage.accessModestringNoReadWriteOnceAccess mode for shared storage PVC (use ReadWriteMany for multi-node access)
coreweave.storage.annotationsmapNo{}Additional annotations for storage resources

Example:

spec: fuzzball: orchestrator: provisioner: enabled: true coreweave: enabled: true storage: class: shared-vast size: 100Gi accessMode: ReadWriteMany
Workflow Data Storage Sizing
Workload TypeRecommended SizeRationale
Light workflows50GiMinimal data processing
Standard workflows100GiTypical data processing needs
Heavy workflows250Gi+Large datasets, intermediate files
Data-intensive500Gi+Big data processing, ML training
Shared PVC Configuration (fuzzball.config.sharedPVC)

The shared PVC (fuzzball-sharedfs) is mounted at /mnt/fuzzball-sharedfs on substrate nodes for internal container image caching across compute nodes. This improves workflow startup times by avoiding repeated image downloads. This storage is not exposed to workflows.

ParameterTypeRequiredDefaultDescription
fuzzball.config.sharedPVC.accessModestringYes-Volume access mode (must be ReadWriteMany)
fuzzball.config.sharedPVC.classstringYes-Storage class name (must be shared-vast)
fuzzball.config.sharedPVC.sizestringYes-Total shared cache size

Example:

spec: fuzzball: config: sharedPVC: accessMode: ReadWriteMany class: shared-vast size: 10Gi
Shared PVC Sizing
Environment TypeRecommended SizeRationale
Testing/Development10GiFew container images
Small Production25GiLimited image variety
Large Production50Gi+Many different images

Slurm Provisioner

See Slurm Integration Documentation for detailed configuration.

ParameterTypeRequiredDefaultDescription
slurm.enabledbooleanYes-Enable Slurm provisioner
slurm.sshHoststringYes-SSH host for remote Slurm instance
slurm.sshPortintegerNo22SSH host port for remote Slurm instance
slurm.usernamestringYes-SSH login username for remote Slurm instance
slurm.passwordstringNo-SSH login password for remote Slurm instance
slurm.sshHostPublicKeystringNo-SSH host public key for remote Slurm instance
slurm.sshPrivateKeyPemstringNo-SSH private key PEM for remote Slurm instance
slurm.sshPrivateKeyPassPhrasestringNo-Passphrase for encrypted SSH private key
slurm.binaryPathstringNo-Custom path to Slurm binaries (if not in $PATH)
slurm.connectionTimeoutintegerNo30SSH connection timeout in seconds
slurm.sudoPathstringNo-Path to sudo binary on compute nodes
slurm.optionsmapNo{}Additional Slurm sbatch options
slurm.skipHostKeyVerificationbooleanNofalseSkip SSH host key verification (not recommended)

Basic example:

spec: fuzzball: orchestrator: provisioner: enabled: true slurm: enabled: true sshHost: slurm-head.example.com sshPort: 22 username: fuzzball-service sshPrivateKeyPem: | <full private key in PEM format> sshHostPublicKey: "slurm-head.example.com ecdsa-sha2-nistp256 AAAAE2..."

PBS Provisioner

See PBS Integration Documentation for detailed configuration.

ParameterTypeRequiredDefaultDescription
pbs.enabledbooleanYes-Enable PBS provisioner
pbs.sshHoststringYes-SSH host for remote PBS instance
pbs.sshPortintegerNo22SSH host port for remote PBS instance
pbs.usernamestringYes-SSH login username for remote PBS instance
pbs.passwordstringNo-SSH login password for remote PBS instance
pbs.sshHostPublicKeystringNo-SSH host public key for remote PBS instance
pbs.sshPrivateKeyPemstringNo-SSH private key PEM for remote PBS instance
pbs.sshPrivateKeyPassPhrasestringNo-Passphrase for encrypted SSH private key
pbs.binaryPathstringNo-Custom path to PBS binaries (if not in $PATH)
pbs.validateSubstratebooleanNofalseValidate substrate before use
pbs.defaultQueuestringNo-Default PBS queue name
pbs.pbsServerstringNo-PBS server hostname
pbs.connectionTimeoutintegerNo30SSH connection timeout in seconds
pbs.optionsmapNo{}Additional PBS qsub options
pbs.sudoPathstringNo-Path to sudo binary on compute nodes
pbs.skipHostKeyVerificationbooleanNofalseSkip SSH host key verification (not recommended)

Basic example:

spec: fuzzball: orchestrator: provisioner: enabled: true pbs: enabled: true sshHost: pbs-head.example.com sshPort: 22 username: fuzzball-service password: secure-password

LOTA Object Storage

Preview Status

Support for CoreWeave LOTA object storage is in preview status and is currently subject to more rapid change to address customer requirements than other features of Fuzzball. If you are interested in using LOTA with Fuzzball on CoreWeave, we recommend contacting CIQ as part of your deployment planning process.

Overview

CoreWeave's LOTA provides S3-compatible object storage for workflow data ingress and egress. Configure LOTA credentials to enable workflows to read from and write to LOTA buckets.

Configuration Parameters

ParameterTypeRequiredDescription
typestringYesMust be s3 for LOTA
secret.access-key-idstringYesLOTA access key ID
secret.secret-access-keystringYesLOTA secret access key
secret.endpointstringYesLOTA endpoint URL (https://cwlota.com)
secret.regionstringYesLOTA bucket region (e.g., ord1, lga1)

Create LOTA Credentials

Step 1: Create Credentials File

Create a YAML file with your LOTA credentials:

# lota-credentials.yaml type: s3 secret: access-key-id: AKIAIOSFODNN7EXAMPLE secret-access-key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY endpoint: https://cwlota.com region: us-east-02a

Replace placeholder values with your actual LOTA credentials from CoreWeave.

Step 2: Create Secret in Fuzzball

$ fuzzball secret create -s user --from-file \ lota-credentials lota-credentials.yaml

The -s user flag makes the secret available to all users in your organization.

Use LOTA in Workflows

Reference the LOTA credentials in workflow volume definitions:

volumes: shared-storage: reference: volume://user/fuzzball-shared-vast/shared-storage ingress: - source: uri: "s3://my-bucket/input/data.txt" secret: secret://user/lota-credentials destination: uri: "file://data.txt" egress: - source: uri: "file://results.tar.gz" destination: uri: "s3://my-bucket/output/results.tar.gz" secret: secret://user/lota-credentials

Replace my-bucket with your LOTA bucket name.

Common Configuration Patterns

Minimal Local Deployment

For testing and development on a local Kubernetes cluster. This deploys the Fuzzball control plane. To run workflows, you'll need to add shared storage (fuzzball.substrate.nfs or fuzzball.config.sharedPVC) and configure compute nodes.

apiVersion: deployment.ciq.com/v1alpha1 kind: FuzzballOrchestrate metadata: name: fuzzball-orchestrate spec: image: username: depot-user password: depot-token exclusive: false ingress: create: domain: localhost.nip.io proxy: type: NodePort database: create: storage: class: local-path keycloak: create: ownerEmail: admin@localhost createDatabase: true tls: certManager: create: {} trustManager: create: {} fuzzball: jetstream: replicas: 1 externalService: type: NodePort storage: class: local-path substrateBridge: log: storage: class: "local-path" dns: externalService: type: NodePort

Production On-Premises Deployment

For production deployments with high availability:

apiVersion: deployment.ciq.com/v1alpha1 kind: FuzzballOrchestrate metadata: name: fuzzball-orchestrate spec: image: username: depot-user password: depot-token exclusive: true ingress: create: domain: fuzzball.company.com proxy: type: LoadBalancer annotations: metallb.io/loadBalancerIPs: 10.0.100.50 database: create: storage: class: longhorn size: 500Gi keycloak: create: ownerEmail: admin@company.com replicas: 2 createDatabase: true tls: certManager: create: {} trustManager: create: {} ingressIssuer: create: letsEncrypt: email: admin@company.com issuer: letsencrypt-prod solvers: - dns01: route53: region: us-east-1 hostedZoneID: Z1234567890ABC fuzzball: cluster: name: production-cluster substrate: nfs: server: nfs.company.com path: /fuzzball/shared destination: /fuzzball/shared orchestrator: replicas: 3 autoscaling: enabled: true minReplicas: 2 maxReplicas: 10 agent: replicas: 5 autoscaling: enabled: true minReplicas: 3 maxReplicas: 20 jetstream: replicas: 3 storage: class: longhorn size: 50Gi

Cloud Deployment with External Services

Using external database and authentication:

apiVersion: deployment.ciq.com/v1alpha1 kind: FuzzballOrchestrate metadata: name: fuzzball-orchestrate spec: image: username: depot-user password: depot-token ingress: external: domain: fuzzball.cloud.com className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod database: external: host: postgres.abc123.us-east-1.rds.amazonaws.com rdsSecretId: arn:aws:secretsmanager:us-east-1:123456789012:secret:fuzzball-db credentials: user: "" # Leave empty when using rdsSecretId password: "" # Leave empty when using rdsSecretId sslMode: verify-full certificate: caCertURL: https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem keycloak: external: url: https://keycloak.cloud.com realmId: 550e8400-e29b-41d4-a716-446655440000 realmName: FuzzballProd username: admin password: keycloak-password ownerEmail: admin@cloud.com fuzzball: config: sharedPVC: accessMode: ReadWriteMany class: efs-sc size: 100Gi orchestrator: provisioner: enabled: true aws: enabled: true region: us-east-1 subnetIDs: - subnet-0123456789abcdef0 securityGroupIDs: - sg-0123456789abcdef0 instanceProfileARN: arn:aws:iam::123456789012:instance-profile/FuzzballSubstrate serviceAccount: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/fuzzball-orchestrator

Managing FuzzballOrchestrate Resources

Viewing Configuration

Check deployment status:

$ kubectl get fuzzballorchestrate NAME STATUS AGE fuzzball-orchestrate Running 10m

View the current configuration:

$ kubectl get fuzzballorchestrate fuzzball-orchestrate -o yaml

View status and events:

$ kubectl describe fuzzballorchestrate fuzzball-orchestrate

Updating Configuration

Edit the configuration in-place (changes are applied immediately):

$ kubectl edit fuzzballorchestrate fuzzball-orchestrate

Apply configuration from a file:

$ kubectl apply -f fuzzball.yaml

Monitoring Deployment

The operator reports deploy and destroy progress directly on the custom resource. Use kubectl describe to see real-time status during any operation:

$ kubectl describe fuzzballorchestrate fuzzball-orchestrate
Note

The operator uses Pulumi internally to manage the underlying Kubernetes and cloud resources. As a result, events and .status.failedResources entries identify resources by Pulumi URN — a stable identifier of the form urn:pulumi:<stack>::<provider>::<type>::<name>. You can treat a URN as an opaque label unique to the specific resource Pulumi is tracking; you only need to recognize that the trailing <name> segment is human-readable (e.g., fuzzball-coldboot, fuzzball-load-default-catalog).

Status Conditions

Two conditions are maintained on the resource:

ConditionStatusReasonMeaning
ProgressingTrueDeployingDeploy in progress
ReadyFalseDeployingDeploy in progress
ProgressingFalseDeployCompleteDeploy finished successfully
ReadyTrueSucceededDeploy finished successfully
ProgressingFalseDeployFailedDeploy failed (see Message for error)
ReadyFalseDeployFailedDeploy failed
ProgressingTrueDestroyingDestroy in progress
ReadyFalseTerminatingDestroy in progress
ProgressingFalseDestroyCompleteDestroy finished — resource pending garbage collection
ProgressingFalseDestroyFailedDestroy failed (see Message for error)
ReadyFalseDestroyFailedDestroy failed

Each condition also carries a Message field summarizing the state in human-readable form: "Deploy in progress" or "Destroy in progress" while the operation is running, "Deploy completed" or "Destroy completed" on success, or the underlying error string when it failed.

Note

After a successful destroy, Ready is not updated — the operator removes the finalizer and Kubernetes garbage-collects the resource. Only Progressing transitions to False / DestroyComplete before the resource disappears.

Progress Field

While a deploy or destroy is running, .status.progress is populated with real-time counters and the currently active resources:

Status: Progress: Phase: Deploying Completed: 153 In Flight: 2 Failed: 0 Elapsed Seconds: 310 Start Time: 2026-04-23T01:00:22Z Current Resources: Kind: Command Op: create Elapsed Seconds: 0 Urn: urn:pulumi:…::fuzzball-coldboot Kind: Job Op: create Elapsed Seconds: 0 Urn: urn:pulumi:…::fuzzball-load-default-catalog

Phase is either Deploying or Terminating. completed, inFlight, and failed count individual infrastructure resource operations. Once the operation finishes, .status.progress is cleared.

Kubernetes Events

The operator emits events on the resource throughout the operation lifecycle. View them at the bottom of kubectl describe output or with:

$ kubectl get events --field-selector involvedObject.name=fuzzball-orchestrate --sort-by='.lastTimestamp'

Deploy events:

EventTypeDescription
DeployStartedNormalDeploy has begun
DeployCompletedNormalDeploy succeeded — includes elapsed time and resource count
ResourceOpFailedWarningA single tracked resource's create/update/replace/delete failed
DeployFailedWarningDeploy failed — see .status.failedResources and operator logs

Destroy events:

EventTypeDescription
DestroyStartedNormalDestroy has begun
DestroyingDependencyNormalA named dependency stack (e.g., Fuzzball, Database) is being torn down
DestroyedDependencyNormalA dependency stack completed teardown
DestroyCompletedNormalAll resources destroyed — includes elapsed time and count
ResourceOpFailedWarningA single tracked resource's create/update/replace/delete failed
DestroyFailedWarningDestroy failed

ResourceOpFailed is emitted per affected resource (not once per operation), so it can fire multiple times during a single Deploy or Destroy. Its message has the form <Kind> <op> failed: <URN> (for example, Job create failed: urn:pulumi:fuzzball::…::fuzzball-load-default-catalog). It is the most precise signal for pinpointing which resource broke before the rolled-up DeployFailed / DestroyFailed event fires.

The same event patterns apply to both FuzzballOrchestrate and FuzzballFederate resources.

Example — successful Orchestrate deploy:

Events: Normal DeployCompleted 0s Deploy completed in 2m11s; 93 resources applied

Example — 164-resource teardown:

Events: Normal DestroyStarted 1m41s Starting Destroy for fuzzball-local-dev Normal DestroyingDependency 1m41s Destroying Identity Normal DestroyingDependency 1m37s Destroying Fuzzball Normal DestroyingDependency 53s Destroying Namespace Normal DestroyingDependency 48s Destroying Database Normal DestroyingDependency 10s Destroying TLS Normal DestroyedDependency 0s Destroyed Database Normal DestroyedDependency 0s Destroyed Fuzzball Normal DestroyedDependency 0s Destroyed Identity Normal DestroyedDependency 0s Destroyed Namespace Normal DestroyedDependency 0s Destroyed TLS Normal DestroyCompleted 0s Destroy completed in 1m44s; 164 resources destroyed

Watch operator logs during deployment:

$ kubectl logs -l app.kubernetes.io/name=fuzzball-operator -n fuzzball-system -f

Important Notes

  1. Resource Scope: FuzzballOrchestrate is a cluster-scoped resource (not namespace-scoped)
  2. Short Names: Can use fb or fuzz as shortcuts in kubectl commands
  3. Mutual Exclusivity:
    • Database: Use either create or external, not both
    • Ingress: Use either create or external, not both
    • Keycloak: Use either create or external, not both
  4. Autoscaling: When enabled, replicas is ignored in favor of minReplicas/maxReplicas
  5. Storage Classes: Ensure specified storage classes exist in your cluster
  6. UUIDs: realmId must be a valid UUID v4 format
  7. NFS Requirements: NFS server must be accessible from all nodes

Troubleshooting

Deployment Fails

When a deploy fails, the Ready and Progressing conditions are both set to False with reason DeployFailed. The Message field on the condition contains the Pulumi diagnostic error:

Status: Conditions: Type: Progressing Status: False Reason: DeployFailed Message: an unhandled error occurred: waiting for RPCs: marshaling properties: awaiting input property "clientId": while getting fuzzball realm: ... Type: Ready Status: False Reason: DeployFailed Message: (same as above) Events: Warning DeployFailed 0s Deploy failed after 5m15s. See .status.failedResources and operator logs.

The Message field is capped at 256 characters; a trailing ... in the output indicates the text was truncated. Consult the operator logs (shown further down) for the full error.

Check .status.failedResources for a structured list of every resource that failed, including the Pulumi URN, the operation type (create, update, replace, or delete), the error message, and how many times it was retried:

$ kubectl get fuzzballorchestrate fuzzball-orchestrate -o jsonpath='{.status.failedResources}' | jq .

Each entry contains:

  • urn — Pulumi URN identifying the specific resource that failed
  • op — operation that was attempted
  • error — the diagnostic error message
  • attemptCount — number of retries before giving up

.status.failedResources is retained on the resource until the next stack operation begins — the operator overwrites the slice with a fresh (empty) one as soon as the next deploy or destroy starts, so capture this output before retrying if you want a post-mortem record.

Check operator logs for additional context:

$ kubectl logs -l app.kubernetes.io/name=fuzzball-operator -n fuzzball-system --tail=100

Check FuzzballOrchestrate status and events:

$ kubectl describe fuzzballorchestrate fuzzball-orchestrate

Pods Not Starting

Check pod events:

$ kubectl get pods -n fuzzball $ kubectl describe pod <pod-name> -n fuzzball

Check PVC status:

$ kubectl get pvc -A

Ingress Not Working

Check ingress configuration:

$ kubectl get ingress -A $ kubectl describe ingress <ingress-name> -n fuzzball

Verify domain DNS resolution and load balancer IP assignment.

See Also