Deployment
Boilerhouse deploys as three Go binaries running against a Kubernetes cluster: the operator, the API server, and the trigger gateway. Kustomize manifests under config/deploy/ contain a reference production layout.
Prerequisites
- Kubernetes 1.26+ cluster
kubectlwith cluster-admin access- A container registry for the three Boilerhouse images
- (Optional)
kustomizeor recentkubectlwith built-in kustomize support
Building Images
Three Dockerfiles live in go/:
cd go
docker build -f Dockerfile.operator -t <registry>/boilerhouse-operator:<tag> .
docker build -f Dockerfile.api -t <registry>/boilerhouse-api:<tag> .
docker build -f Dockerfile.trigger -t <registry>/boilerhouse-trigger:<tag> .
docker push <registry>/boilerhouse-operator:<tag>
docker push <registry>/boilerhouse-api:<tag>
docker push <registry>/boilerhouse-trigger:<tag>For minikube development, build directly inside the cluster's Docker daemon:
eval "$(minikube docker-env -p boilerhouse)"
cd go
docker build -f Dockerfile.operator -t boilerhouse-operator:latest .
docker build -f Dockerfile.api -t boilerhouse-api:latest .
docker build -f Dockerfile.trigger -t boilerhouse-trigger:latest .Install CRDs
Apply the four Custom Resource Definitions generated from the Go types:
kubectl apply -f config/crd/bases-go/This installs:
boilerhouseworkloads.boilerhouse.devboilerhousepools.boilerhouse.devboilerhouseclaims.boilerhouse.devboilerhousetriggers.boilerhouse.dev
Deploy
The reference manifests under config/deploy/ install the namespace, RBAC, operator, API, and trigger gateway:
kubectl apply -k config/deploy/This applies:
namespace.yaml— theboilerhousenamespace- CRDs from
config/crd/bases/ operator.yaml— ServiceAccount, ClusterRole, ClusterRoleBinding, and the operator Deploymentapi.yaml— the API Deployment and its Servicetrigger.yaml— the trigger gateway Deployment
Edit image references in config/deploy/*.yaml to point at your registry before applying.
RBAC
The operator needs cluster-scoped permissions to manage Pods, Services, ConfigMaps, Secrets, PersistentVolumeClaims, Events, NetworkPolicies, Leases, and the four Boilerhouse CRDs. The reference ClusterRole in config/deploy/operator.yaml:
rules:
- apiGroups: ["boilerhouse.dev"]
resources: ["*"]
verbs: ["*"]
- apiGroups: ["boilerhouse.dev"]
resources: ["*/status"]
verbs: ["get", "update", "patch"]
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets", "persistentvolumeclaims", "events"]
verbs: ["*"]
- apiGroups: ["networking.k8s.io"]
resources: ["networkpolicies"]
verbs: ["*"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["*"]The API and trigger gateway share the boilerhouse-operator ServiceAccount in the reference manifests. Tighten this in production if you want per-component RBAC.
High Availability
The operator supports Kubernetes Lease-based leader election. Deploy with multiple replicas:
spec:
replicas: 2Only the leader reconciles; other replicas are on standby and take over automatically on leader failure. Disable with LEADER_ELECT=false for single-instance deployments.
The API server is stateless — scale it horizontally with no coordination. The trigger gateway currently does not coordinate between replicas, so run a single replica to avoid duplicate event handling.
Snapshots PVC
The operator expects a PVC named boilerhouse-snapshots for tenant overlay storage, mounted by the boilerhouse-snapshot-helper Pod. Add a PVC manifest to your deploy:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: boilerhouse-snapshots
namespace: boilerhouse
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 50Gi
storageClassName: <your-storage-class>Use a storage class backed by a durable provider (EBS, GCP PD, Longhorn, etc.). ReadWriteOnce is sufficient because only the single helper Pod mounts the volume.
Production Checklist
Security
- [ ] Set
BOILERHOUSE_API_KEYfor API authentication - [ ] Use
restrictednetwork access for untrusted workloads - [ ] Put the API server behind an Ingress with TLS termination
- [ ] Create tenant credential Secrets in the
boilerhousenamespace, not client-side - [ ] Tighten RBAC if the API / trigger gateway don't need the full operator ClusterRole
Storage
- [ ] Provision the
boilerhouse-snapshotsPVC with a durable storage class - [ ] Choose a storage class that provides encryption at rest if required
Observability
- [ ] Scrape
:9464/metricson the operator Pod - [ ] Configure
OTEL_EXPORTER_OTLP_ENDPOINTon all three binaries - [ ] Set
LOG_LEVEL=infofor production - [ ] Monitor key metrics:
boilerhouse.pool.depth— pool healthboilerhouse.tenant.claim.duration— claim latencyboilerhouse.instances(phase=Running) — live instance countkube_pod_status_phase— standard Pod-level health from kube-state-metrics
Resources
- [ ] Apply a
ResourceQuotato theboilerhousenamespace to cap total CPU/memory - [ ] Size workload resources (CPU, memory, disk) appropriately
- [ ] Configure pool sizes based on expected concurrency
- [ ] Set
maxFillConcurrencyto avoid swamping the scheduler during pool refill
Networking
- [ ] Configure domain allowlists for restricted workloads
- [ ] Create Kubernetes Secrets for credential injection
- [ ] If workloads need external access, expose their Services via Ingress / LoadBalancer