Skip to main content

ButlerConfig

ButlerConfig is the singleton platform configuration resource. It defines platform-wide defaults that affect all teams and tenant clusters.

API Version

butler.butlerlabs.dev/v1alpha1

Scope

Cluster

Short Name

bc

Description

ButlerConfig is a singleton resource named butler that configures platform-wide behavior. It is created automatically during bootstrap and lives for the lifetime of the management cluster. Operators edit it to:

  • Switch how tenant API servers are exposed (LoadBalancer, Ingress, or Gateway mode)
  • Set default control plane resource limits to prevent etcd starvation when running many tenants
  • Enable multi-tenancy enforcement so all clusters must belong to a Team
  • Configure a default infrastructure provider for teams that do not specify their own
  • Set up GitOps integration to enable Flux-based workflows and cluster export
  • Configure the Image Factory for automatic OS image syncing to providers
  • Define default addon versions so tenant clusters inherit consistent versions
  • Set default team resource limits applied to new Teams

Changes to ButlerConfig take effect on new TenantClusters. Existing clusters retain their settings unless explicitly updated.

Common Operations

Switch Control Plane Exposure Mode

To move from LoadBalancer mode (1 IP per tenant) to Ingress mode (shared IP via SNI):

spec:
controlPlaneExposure:
mode: Ingress
hostname: "*.k8s.platform.example.com"
ingressClassName: haproxy
controllerType: haproxy

Set Default Control Plane Resources

Without resource requests, tenant control plane pods run as BestEffort QoS and can be starved by other workloads. At 10+ tenants, set explicit resources to prevent etcd-driven instability:

spec:
defaultControlPlaneResources:
apiServer:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "2"
memory: "1Gi"
controllerManager:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "256Mi"
scheduler:
requests:
cpu: "25m"
memory: "32Mi"
limits:
cpu: "250m"
memory: "128Mi"

Enable Multi-Tenancy

spec:
multiTenancy:
mode: Enforced
defaultTeamLimits:
maxClusters: 5
maxWorkersPerCluster: 10
maxTotalCPU: "64"
maxTotalMemory: "128Gi"

Configure Default Provider

spec:
defaultProviderConfigRef:
name: harvester-prod

Spec

FieldTypeRequiredDefaultDescription
multiTenancyobjectNoMulti-tenancy configuration
defaultNamespacestringNobutler-tenantsNamespace for TenantClusters when not using Teams
defaultProviderConfigRefobjectNoReference to the default ProviderConfig
defaultTeamLimitsobjectNoDefault resource limits for new Teams
defaultAddonVersionsobjectNoDefault addon versions for new TenantClusters
gitProviderobjectNoGit provider for GitOps operations
controlPlaneExposureobjectNoHow tenant control planes are exposed
defaultControlPlaneResourcesobjectNoDefault resource limits for tenant control plane components
observabilityobjectNoPlatform-level observability pipeline
imageFactoryobjectNoButler Image Factory connection
sshAuthorizedKeystringNoDefault SSH public key for non-Talos worker nodes

multiTenancy

FieldTypeRequiredDefaultDescription
modestringNoDisabledMulti-tenancy mode: Enforced, Optional, or Disabled

Modes:

ModeDescription
EnforcedAll TenantClusters must belong to a Team. Teams must exist before clusters. Recommended for enterprise
OptionalTeams are available but not required. Clusters can exist in the default namespace
DisabledTeam functionality is off. All clusters exist in the default namespace

defaultTeamLimits

Platform-wide defaults applied to new Teams. Individual Teams can override these in their spec.

FieldTypeRequiredDescription
maxClustersintegerNoMaximum TenantClusters per Team
maxWorkersPerClusterintegerNoMaximum workers per TenantCluster
maxTotalCPUquantityNoMaximum total CPU across all clusters
maxTotalMemoryquantityNoMaximum total memory across all clusters
maxTotalStoragequantityNoMaximum total storage across all clusters

defaultAddonVersions

Default versions for addons when TenantClusters do not specify versions.

FieldTypeDescription
ciliumstringCilium version
metallbstringMetalLB version
certManagerstringcert-manager version
longhornstringLonghorn version
traefikstringTraefik version
fluxcdstringFluxCD version

controlPlaneExposure

Configures how tenant API servers are exposed. Set during bootstrap and inherited by all TenantClusters.

FieldTypeRequiredDefaultDescription
modestringNoLoadBalancerExposure mode: LoadBalancer, Ingress, or Gateway
hostnamestringIngress/GatewayWildcard domain for tenant API servers (e.g., *.k8s.platform.example.com)
ingressClassNamestringNoIngress class when mode is Ingress
controllerTypestringNoIngress controller type for TLS passthrough: haproxy, nginx, traefik, generic
gatewayRefstringGatewayGateway resource reference (format: namespace/name)

Exposure Modes:

ModeIPsRoutingtcp-proxy
LoadBalancer1 per tenantDirect TCPNot required
IngressSharedSNI via Ingress controllerAuto-enabled
GatewaySharedSNI via Gateway API TLSRouteAuto-enabled

defaultControlPlaneResources

Default resource allocations for tenant control plane components. Applied to new TenantClusters that do not specify per-cluster overrides. If not set, pods run with no resource requests (BestEffort QoS).

Each component (apiServer, controllerManager, scheduler) has requests and limits with cpu and memory fields.

FieldTypeDescription
apiServerobjectkube-apiserver resources
controllerManagerobjectkube-controller-manager resources
schedulerobjectkube-scheduler resources

gitProvider

Configures the Git provider for GitOps operations (cluster export, Flux enablement, addon management via Git).

FieldTypeRequiredDefaultDescription
typestringYesProvider type: github, gitlab, bitbucket
urlstringNohttps://api.github.comGit provider API URL
organizationstringNoDefault organization/group for repositories
secretRefobjectYesReference to Secret containing credentials

Secret keys by provider:

ProviderRequired Keys
githubtoken (PAT with repo scope)
gitlabtoken (PAT with api scope)
bitbucketusername, app-password

imageFactory

Configures the Butler Image Factory for automatic OS image syncing.

FieldTypeRequiredDefaultDescription
urlstringYesBase URL of the Image Factory API
credentialsRefobjectNoReference to Secret containing the apiKey
defaultSchematicIDstringNoDefault schematic when TenantClusters do not specify one
autoSyncboolNotrueAuto-sync images when a TenantCluster references one not yet on the target provider

observability

Platform-level observability pipeline configuration.

FieldTypeDescription
pipelineobjectCentralized observability pipeline cluster
pipeline.clusterRefobjectReference to the TenantCluster serving as the pipeline
pipeline.logEndpointstringVector aggregator ingestion URL
pipeline.metricEndpointstringRemote-write endpoint for metrics
pipeline.traceEndpointstringOTLP endpoint for traces
collectionobjectDefault collection settings for tenant clusters
collection.autoEnrollobjectAuto-install agents: vectorAgent, prometheus, otelCollector (booleans)
collection.logsobjectLog collection: podLogs, journald, kubernetesEvents (booleans)
collection.metricsobjectMetric collection: enabled (bool), retention (string, e.g., "15d")

Status

FieldTypeDescription
conditions[]ConditionStandard Kubernetes conditions
observedGenerationintegerLast observed generation
teamCountintegerCurrent number of Teams
clusterCountintegerCurrent number of TenantClusters
gitProviderobjectGit provider connection status
controlPlaneExposureModestringActive exposure mode
tcpProxyRequiredboolWhether tcp-proxy is auto-enabled (true for Ingress/Gateway)
observabilityobjectObservability pipeline status

Examples

LoadBalancer Mode (Default)

Simplest configuration for on-premises deployments with MetalLB providing 1 IP per tenant:

apiVersion: butler.butlerlabs.dev/v1alpha1
kind: ButlerConfig
metadata:
name: butler
spec:
multiTenancy:
mode: Disabled
defaultProviderConfigRef:
name: harvester-prod
controlPlaneExposure:
mode: LoadBalancer

Ingress Mode with HAProxy

Share a single IP across tenants using SNI-based routing through an HAProxy Ingress controller:

apiVersion: butler.butlerlabs.dev/v1alpha1
kind: ButlerConfig
metadata:
name: butler
spec:
multiTenancy:
mode: Enforced
defaultProviderConfigRef:
name: harvester-prod
controlPlaneExposure:
mode: Ingress
hostname: "*.k8s.butlerlabs.dev"
ingressClassName: haproxy
controllerType: haproxy
defaultTeamLimits:
maxClusters: 5
maxWorkersPerCluster: 10

Resource-Tuned for 10+ Tenants

Production configuration with control plane resource limits to prevent etcd starvation under multi-tenant load:

apiVersion: butler.butlerlabs.dev/v1alpha1
kind: ButlerConfig
metadata:
name: butler
spec:
multiTenancy:
mode: Enforced
defaultProviderConfigRef:
name: harvester-prod
controlPlaneExposure:
mode: LoadBalancer
defaultControlPlaneResources:
apiServer:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "2"
memory: "1Gi"
controllerManager:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "256Mi"
scheduler:
requests:
cpu: "25m"
memory: "32Mi"
limits:
cpu: "250m"
memory: "128Mi"
defaultTeamLimits:
maxClusters: 10
maxWorkersPerCluster: 20
maxTotalCPU: "128"
maxTotalMemory: "256Gi"
defaultAddonVersions:
cilium: "1.17.0"
metallb: "0.14.9"
longhorn: "1.7.2"
certManager: "1.16.3"

See Also