Deploying on Google Kubernetes Engine (GKE)¶
K-Lake installs on GKE with the same Helm chart as every other cluster. This page covers what is specific to Google Cloud. For the install itself see Production deployment; for how many extractors to run see Sizing for ingest time.
Validated on GKE Standard 1.36 in europe-west2, with the Google Cloud
Storage source and the private console address from 0.1.53.
Before you start¶
- A GKE Standard cluster on x86 nodes. Autopilot has not been validated.
SMB and NFS sources mount shares from inside the pod, which needs
CAP_SYS_ADMIN; GKE Standard admits it. If you index only object storage, setsources.smbEnabled=falseand the pods run without it. - vCPU quota. A new project's default is 24 vCPU across all regions. Three n2-standard-8 nodes use all of it. Request more before you size above that.
-
Zone capacity. A zone can run out of a machine type. If node creation sits on
ZONE_RESOURCE_POOL_EXHAUSTED, add a node pool in another zone of the same region: -
Outbound HTTPS to
ghcr.iofor the images.
gcloud container clusters create and get-credentials switch your current
kubectl context to the new cluster.
Recommended cluster¶
| Starting point | |
|---|---|
| Node pool | 3 × n2-standard-8 (8 vCPU, 32 GB) |
| Workload Identity | Enabled (--workload-pool=<project>.svc.id.goog), needed for keyless Google Cloud Storage |
| Storage | The default standard-rwo StorageClass. Leave global.storageClass empty. |
That holds the small tier with one kdoc2 replica, the embedding service and
up to 11 klex extractors. Measured: about 18,000 mixed documents an hour.
Install¶
helm install kdbl oci://ghcr.io/kdbl-consulting/kdbl --version <X.Y.Z> \
--namespace kdbl --create-namespace \
--set tier=small \
--set licence.blob="$KDBL_LICENCE"
All pods were ready about 2.5 minutes after install. Some restart once or twice while the database comes up; that is expected.
Console access¶
Leave console.access at its default, internalLB. From 0.1.53 the chart
asks GKE for an internal load balancer
(networking.gke.io/load-balancer-type: Internal): a private IP reachable from
the VPC, peered networks and VPN.
Chart 0.1.52 and earlier
The internal load-balancer annotation was Azure's only, and GKE ignores
it, so internalLB got a public IP open to every address. On those
versions use --set console.access=portForward or an ingress, and check
any existing install with kubectl get svc kdbl-ui -n kdbl. See
Upgrading.
GPU OCR (optional)¶
A GPU makes kdoc2, which reads scanned pages and the pictures inside Office files, several times faster. Add one GPU node pool and give the kdoc2 fleet the GPU:
gcloud container node-pools create gpu-l4 --cluster <cluster> --zone <zone> \
--machine-type g2-standard-8 \
--accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \
--num-nodes 1 --workload-metadata=GKE_METADATA
extract:
additionalFleets:
- plugin: kdoc2
replicas: 1
gpu: {enabled: true} # 4 OCR pipelines per pod by default (gpu.ocrPipelines)
- GKE installs the driver (
gpu-driver-version=latest), taints the node, and lets only pods that ask for a GPU onto it. The kdoc2 pod asks for one. - One pod runs several OCR pipelines on the one GPU. You do not need GPU time-sharing. See Sizing for ingest time for the figures.
- A new project's GPU quota is 0. Ask for GPUs (all regions) under IAM & Admin → Quotas before you create the pool.
- Capacity varies by zone and hour. In October 2026 on-demand and Spot L4s
were both unavailable in europe-west2-a and -b for hours
(
ZONE_RESOURCE_POOL_EXHAUSTED), while europe-west4-a had Spot capacity. A node pool that cannot get a GPU keeps retrying quietly. To find capacity quickly, create a throwaway VM with the same machine type in each zone you could use; that fails within seconds. - A Spot GPU costs about 40% less, but Google can reclaim it at any time. kdoc2 work in flight is retried when the pod returns. Use Spot when a slower second pass is acceptable.
- Chart 0.1.52 and earlier: the GPU build of kdoc2 cannot find GKE's
driver, and the pod stops with
libcuda.so.1: cannot open shared object file. Upgrade to 0.1.53 or later, which finds it.
Indexing Google Cloud Storage¶
From 0.1.53, gs:// is a native source type (see
Sources). The keyless route uses GKE
Workload Identity:
-
Create a Google service account and let it read the bucket:
-
Let the chart's
kdbl-source-readerKubernetes service account act as it: -
Install or upgrade with
--set sources.workloadIdentity.gcpServiceAccount=kdbl-gcs-reader@<project>.iam.gserviceaccount.com. -
Allow the tenant to use the deployment's identity (cluster admin):
-
Add the source with no key, then crawl:
Adding a source does not start a crawl; run crawl (or start one from the
console).
Many organisations block service-account key creation by policy
(iam.managed.disableServiceAccountKeyCreation), which is another reason to
prefer Workload Identity. A JSON key also works: add-gcs --key-file.
Before 0.1.53, index a bucket through the S3 source and Cloud Storage's
S3-compatible endpoint, with an HMAC key for a service account that has
roles/storage.objectViewer:
gcloud storage hmac create <sa-email>
echo "<hmac-secret>" | kdbl-control source add-s3 \
--source-id s3://<bucket> --bucket <bucket> \
--endpoint-url https://storage.googleapis.com --region auto \
--access-key-id <hmac-access-id> --secret-access-key-stdin
Cleaning up¶
helm uninstall leaves the database and cache PersistentVolumeClaims, which
keep their disks. Delete them, then the cluster:
helm uninstall kdbl -n kdbl
kubectl delete pvc --all -n kdbl # waits until the disks are released
gcloud container clusters delete <cluster> --zone <zone>
Delete the cluster only after the claims are gone. A cluster deleted while its volumes are still attached leaves the disks behind, still billed. Check for them afterwards and delete any that remain: