Skip to content

Kubernetes Deployment ​

The Helm chart configures a three-replica BoilStream cluster. Object storage holds published files and completed metadata backups. Hydrated historical files are caches; unpublished hot rows have a separate durability boundary. A persistent volume does not replace the configured recovery path. For write and recovery limits, see Reliability and completion.

Use the chart and example values supplied with your server release.

OCI artifact: oci://ghcr.io/boilingdata/charts/boilstream

Prerequisites ​

  • Kubernetes 1.27 or newer.
  • cert-manager 1.13 or newer, or equivalent pre-created public and private TLS Secrets.
  • Envoy Gateway 1.2 or newer with the configured GatewayClass.
  • An S3-compatible bucket and workload identity or a Secret containing access_key and secret_key.
  • A superadmin password Secret.
  • A stable key Secret containing two distinct values:
    • encryption_key: 32 random bytes encoded as 64 hexadecimal characters;
    • token_signing_key: 32 random bytes encoded as base64.
    • cluster_token: an independent random token of at least 32 characters.

Keep both keys in an external secret manager and restore them unchanged. The encryption key protects recovery state. Changing the signing key invalidates outstanding HTTP ingestion, auth, and PGWire bootstrap tokens.

bash
kubectl -n boilstream create secret generic boilstream-encryption \
  --from-literal=encryption_key="$(openssl rand -hex 32)" \
  --from-literal=token_signing_key="$(openssl rand -base64 32)" \
  --from-literal=cluster_token="$(openssl rand -base64 32)"

Production values ​

Start from the EKS or Hetzner example shipped with the chart. The following fields define the required topology; replace every example identity and endpoint:

yaml
replicas: 3
domain: data.example.com

image:
  repository: registry.example.com/boilstream
  tag: <release-version>
  # Replace this non-resolving example with the published release digest.
  digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
  pullPolicy: IfNotPresent

configRenderer:
  image:
    # Replace with the published Python multi-architecture manifest digest.
    digest: sha256:0000000000000000000000000000000000000000000000000000000000000000

s3:
  endpoint: https://s3.example.com # empty for AWS S3
  bucket: company-boilstream-prod
  region: eu-central-1
  prefix: prod/
  forcePathStyle: true
  existingCredsSecret: boilstream-s3-creds
  accessKey: ""
  secretKey: ""

encryptionKey:
  existingSecret: boilstream-encryption
  secretKey: encryption_key

tokenSigningKey:
  secretKey: token_signing_key # same Secret by default

cpMetadata:
  enabled: true
  ducklakeMultiRaft: true
  usersMultiRaft: true
  tokenSecretKey: cluster_token # same Secret by default
  s3Dr:
    enabled: true
    clusterId: company-prod-eu-1
    backend: primary-s3
    prefix: quack-dr
    durabilityMode: strict
    bootstrapMode: new
    recoveryEpoch: 1
    recoveryId: company-prod-eu-1-first-install

clusterTls:
  enabled: true
  issuer:
    name: boilstream-ca-issuer
    kind: ClusterIssuer

httpIngestion:
  enabled: true
  port: 8446
  allowedOrigins:
    - https://data.example.com

persistence:
  enabled: false

gateway:
  enabled: true
  className: eg

The chart rejects partial clustered configurations. clusterId is the durable identity of one logical cluster and must never be reused for another. bootstrapMode: new with epoch 1 is only for the first installation. Once the first recovery generation exists, change the mode to restore. When image.digest is set, the chart renders repository@sha256:…; image.tag is ignored and remains only a readable release label. The config-renderer init image follows the same rule and must also be digest-pinned.

Install ​

bash
helm install boilstream oci://ghcr.io/boilingdata/charts/boilstream \
  --version '<chart-version>' \
  --namespace boilstream --create-namespace \
  --values my-values.yaml \
  --set-string image.digest='sha256:<release-image-digest>' \
  --set-string configRenderer.image.digest='sha256:<python-image-digest>'

Before installing or upgrading, validate the exact values:

bash
helm lint oci://ghcr.io/boilingdata/charts/boilstream \
  --version '<chart-version>' \
  --values my-values.yaml

Public endpoints ​

InterfaceEndpointPurpose
Web and authhttps://data.example.com/Sign-in, administration, and credential vending
HTTP/2 Arrowhttps://data.example.com:8446/ingest/<token>Browser and service ingestion with a scoped token
Kafkadata.example.com:9092Kafka-compatible produce and consume
FlightRPCdata.example.com:50051Arrow Flight ingestion
FlightSQLdata.example.com:50250Arrow Flight SQL queries
PGWireper-pod ports beginning at 15432PostgreSQL clients using vended credentials
Managed DuckDBvended by the BoilStream extensionNative DuckDB catalog queries over server TLS and a short-lived capability

Use vended connection metadata instead of assembling PGWire or managed DuckDB endpoints. The public extension authenticates to BoilStream and handles the managed DuckDB endpoint, TLS verification, catalog identity, and capability refresh. External clients do not receive a reusable client certificate or private key.

Upgrades and failures ​

Upgrade one replica at a time and wait for readiness before continuing:

bash
helm upgrade boilstream oci://ghcr.io/boilingdata/charts/boilstream \
  --version '<chart-version>' \
  --namespace boilstream \
  --values my-values.yaml \
  --set-string image.digest='sha256:<release-image-digest>' \
  --set-string configRenderer.image.digest='sha256:<python-image-digest>'
kubectl -n boilstream rollout status statefulset/boilstream

The default disruption budget keeps two of three replicas available during voluntary maintenance. Spread replicas across nodes or failure domains when the platform permits.

A client connected to a failed pod must reconnect; existing TCP sessions are not promised to migrate. With quorum, metadata operations continue through healthy replicas. Without quorum, metadata-dependent admission fails closed. A replacement pod catches up from peers without a PVC.

For complete local-state loss, fence the old incarnation, increment the recovery epoch once, set a new recovery ID on every replica, and restore with the original cluster ID, S3 prefix, and encryption key. The recovery point is the latest completed generation.

Verify ​

bash
kubectl -n boilstream rollout status statefulset/boilstream
kubectl -n boilstream get pods,gateway,certificate
boilstream-admin cluster status

Before admitting production traffic, exercise authentication, catalog vending, every enabled ingestion/query interface, a leader failover, a rolling restart, and an S3-only restore in a staging cluster built from the same release artifacts.