The Helm chart deploys bouine as a StatefulSet with headless Service for gossip peer discovery. Source: deploy/helm/bouine/.

Install

helm repo add bouine https://charts.bouine.org
helm repo update

helm install bouine bouine/bouine \
  --namespace bouine --create-namespace \
  --set "config.upstream_pools[0].name=app" \
  --set "config.upstream_pools[0].targets[0]=app.default.svc:8080" \
  --set "config.routes[0].pool=app"

To install from a local checkout, replace bouine/bouine with the chart directory deploy/helm/bouine.

Preconfigured value files

The chart ships with several value profiles for common deployment patterns:

FileProfileUse case
values-dev.yamlDevelopmentMinimal resources, single replica, no warm tier
values-ha.yamlHigh availability5 replicas, PDB, topology spread, larger resources
values-production.yamlProductionHardened security, autoscaling, service monitors, SLO alerts

Use them with -f:

helm install bouine bouine/bouine \
  -n bouine --create-namespace \
  -f values-production.yaml \
  --set "config.upstream_pools[0].name=app" \
  --set "config.upstream_pools[0].targets[0]=app.default.svc:8080"

All values

Image

KeyDefaultDescription
image.repositorybouinecache/bouineContainer image repository (Docker Hub)
image.tag"" (appVersion)Image tag; defaults to chart’s appVersion
image.pullPolicyIfNotPresentImage pull policy
image.pullSecrets[]List of Kubernetes Secret names for pulling from private registries
nameOverride""Override chart name
fullnameOverride""Override fully qualified name

Replicas

KeyDefaultDescription
replicaCount3Number of StatefulSet replicas

bouine config

Rendered into a ConfigMap and mounted at /etc/bouine/config.yaml.

KeyDefaultDescription
config.listen.http":80"HTTP listener
config.listen.https":443"HTTPS listener. Set to "" to disable the TLS plane (e.g. when TLS terminates at an upstream proxy/LB) — the chart then drops the StatefulSet containerPort, the data-plane Service port, and the NetworkPolicy rule with it
config.listen.admin":9000"Admin API listener
config.listen.cluster":8443"Gossip cluster port
config.listen.max_connections4096Cap on simultaneously open data-plane connections. Under HTTP/1.1 a parked handler holds its connection, so this bounds the in-flight pile a slow origin can cause; over-limit connections get 503 + close at accept. Production / HA values: 8192 / 16384.
config.listen.read_timeout30sPer-request header/body read bound — the slowloris defense (since v0.5.9). Raise it for slow mobile clients or large uploads
config.tls.certs[]TLS certificate list; mount via Secret
config.storage.hot_max_bytes2GiBRAM cache size
config.storage.warm_dir/var/lib/bouineWarm-tier mmap directory
config.storage.warm_max_bytes20GiBWarm-tier size limit
config.cluster.join[]Seed addresses (auto-populated from headless Service DNS)
config.cluster.hop_limit2Max peer-fetch hops (strong mode)
config.upstream_pools[]Upstream pool definitions
config.routes[]Route definitions

Services

The chart renders three Services: a data-plane Service, a dedicated admin Service, and a headless Service for gossip peer discovery.

The dedicated admin Service (ClusterIP by default) splits the admin plane (metrics, pprof, /drain, admin API) off the data-plane Service — so exposing the data plane via LoadBalancer can never expose the admin surface. Prometheus ServiceMonitors scrape via the admin Service (label bouine-cache.io/plane: management).

KeyDefaultDescription
service.typeClusterIPKubernetes Service type
service.httpPort80HTTP port
service.httpsPort443HTTPS port
service.adminPort9000Admin port (exposed on the admin Service)
service.annotations{}Annotations on the data-plane Service
service.labels{}Extra labels on the data-plane Service
service.loadBalancerSourceRanges[]Restrict ingress CIDRs when type: LoadBalancer
service.externalTrafficPolicy""Local preserves the client source IP and avoids a second node hop (recommended for type: LoadBalancer)
adminService.typeClusterIPAdmin-plane Service type
adminService.port"" (falls back to service.adminPort)Admin-plane Service port
adminService.annotations{}Annotations on the admin Service
adminService.labels{}Extra labels on the admin Service
headlessService.clusterPort8443Gossip peer discovery port

Resources

KeyDefaultDescription
resources.requests.cpu1000mCPU request
resources.requests.memory2GiMemory request
resources.limits.cpu4CPU limit
resources.limits.memory8GiMemory limit

Go runtime tuning

KeyDefaultDescription
goMemLimit"" (auto)GOMEMLIMIT — auto-computed as 75% of resources.limits.memory
goGC100GOGC — Go GC target percentage

Pod configuration

KeyDefaultDescription
terminationGracePeriodSeconds40Grace period for shutdown sequencer
podDisruptionBudget.enabledtrueEnable PDB
podDisruptionBudget.minAvailable2Minimum available pods during disruption
podDisruptionBudget.annotations{}PDB annotations
topologySpreadConstraintsZone + hostname spreadSpreads pods across availability zones and hosts (ScheduleAnyway)
podAnnotations / podLabels{}Pod metadata
statefulsetAnnotations / statefulsetLabels{}StatefulSet metadata (wins over commonLabels on key collision)
affinity{}Affinity rules (explicit affinity takes precedence over topology spread)
nodeSelector{}Node selector
tolerations[]Tolerations
priorityClassName""Pod priority class
dnsPolicy / dnsConfig"" / {}DNS policy/config overrides
podManagementPolicyParallelParallel pod start (correct for strong-mode gossip join); maxUnavailable: 1 rolls one pod at a time
updateStrategy.typeRollingUpdateStatefulSet update strategy
updateStrategy.maxUnavailable1Max unavailable pods during rolling update
minReadySeconds30Seconds a pod must be ready before entering Service endpoints — lets the cluster ring converge

Metadata and labels

KeyDefaultDescription
commonLabels{}Labels applied to every rendered resource
commonAnnotations{}Annotations applied to every rendered resource
autoscaling.annotations{}HPA annotations
serviceMonitor.annotations{}ServiceMonitor annotations
prometheusRule.annotations / .labels{}PrometheusRule metadata
ingress.annotations{}Ingress annotations
networkPolicy.annotations{}NetworkPolicy annotations
warmVolume.annotations / .labels{}Warm PVC metadata
serviceAccount.annotations{}ServiceAccount annotations

Warm volume retention

KeyDefaultDescription
persistentVolumeClaimRetentionPolicy.enabledfalsePreserve warm-tier data across StatefulSet changes when enabled
persistentVolumeClaimRetentionPolicy.whenScaledRetainPVC retention when scaled
persistentVolumeClaimRetentionPolicy.whenDeletedRetainPVC retention when deleted

Security context

KeyDefaultDescription
podSecurityContext.runAsNonRoottrueRun as non-root
podSecurityContext.runAsUser65534UID (nobody)
containerSecurityContext.readOnlyRootFilesystemtrueRead-only root FS
containerSecurityContext.allowPrivilegeEscalationfalseNo privilege escalation

Probes

KeyDefaultDescription
startupProbe.httpGet.path/readyzStartup probe endpoint
startupProbe.periodSeconds10Startup check interval
startupProbe.failureThreshold180Max failures (30 min timeout)
readinessProbe.httpGet.path/readyzReadiness endpoint
readinessProbe.periodSeconds5Readiness check interval
livenessProbe.httpGet.path/healthzLiveness endpoint
livenessProbe.periodSeconds10Liveness check interval

Autoscaling

KeyDefaultDescription
autoscaling.enabledfalseEnable HorizontalPodAutoscaler
autoscaling.minReplicas3Minimum pod count
autoscaling.maxReplicas6Maximum pod count
autoscaling.cpuTrigger.averageUtilization70CPU target for scale-up
autoscaling.scaleDownStabilizationSeconds120Scale-down stabilization window
autoscaling.annotations{}HPA annotations

GitOps note. With autoscaling.enabled: true the StatefulSet does not render spec.replicas — the HPA owns the replica count. ArgoCD users should add an ignoreDifferences entry for /spec/replicas on the StatefulSet to stop perpetual drift.

Warm volume

KeyDefaultDescription
warmVolume.enabledtrueCreate a PVC for the warm tier
warmVolume.size50GiPVC size
warmVolume.storageClass""Storage class (default = cluster default)
warmVolume.annotations{}PVC annotations
warmVolume.labels{}PVC labels

Observability

KeyDefaultDescription
serviceMonitor.enabledfalseCreate a Prometheus ServiceMonitor (scrapes via the dedicated admin Service)
serviceMonitor.interval60sScrape interval
serviceMonitor.scrapeTimeout""Scrape timeout (default)
serviceMonitor.relabelings[]Endpoint relabelings
serviceMonitor.metricRelabelings[]Metric relabelings — see native histogram cardinality for a recipe
serviceMonitor.labels{}Extra labels for ServiceMonitor
serviceMonitor.annotations{}ServiceMonitor annotations
networkPolicy.enabledfalseCreate NetworkPolicy to isolate admin port
prometheusRule.enabledfalseCreate PrometheusRule with SLO alert thresholds (thresholds overridable)
ingress.enabledfalseCreate an Ingress resource

Cloudflare CDN propagation

KeyDefaultDescription
cloudflare.apiTokenSecretName""Name of the Secret containing CF_API_TOKEN
cloudflare.apiTokenSecretKeyCF_API_TOKENKey inside the Secret

See Cloudflare CDN propagation for config-level settings (config.cloudflare.*).

Extra environment variables

KeyDefaultDescription
extraEnv[]Additional env vars for the bouine container

Example — inject admin token from a Secret:

extraEnv:
  - name: BOUINE_ADMIN_TOKEN
    valueFrom:
      secretKeyRef:
        name: bouine-admin-token
        key: token

Service account

KeyDefaultDescription
serviceAccount.createtrueCreate a dedicated service account for the bouine pods
serviceAccount.automountfalseAuto-mount the service account token. Enable for IRSA / workload identity (AWS EKS, GCP workload identity).
serviceAccount.annotations{}Annotations to add to the service account (e.g. eks.amazonaws.com/role-arn)
serviceAccount.name""Name of an existing service account to use when create is false

Extra volumes

KeyDefaultDescription
extraVolumes[]Additional volumes for the bouine pod (e.g. TLS secrets, custom CA bundles, static files)
extraVolumeMounts[]Additional volume mounts for the bouine container

Example — mount TLS certs from a Secret:

extraVolumes:
  - name: tls-certs
    secret:
      secretName: bouine-tls
extraVolumeMounts:
  - name: tls-certs
    mountPath: /etc/bouine/tls
    readOnly: true