Standard BYOC Setup
This guide takes you from an empty cloud account to a running Standard BYOC cluster. Work through the runlist in order and check off each step as you go. Before you start, check the requirements and size your cluster.
For each cluster, turbopuffer provides a BYOC kit with everything the runlist needs. If you get stuck on a step, ask us in your shared Slack channel.
Your kit contents
byoc-kit
├── README.md
├── aws
│ ├── main.tf
│ └── turbopuffer.tfvars
├── cosign.pub
├── gcp
│ ├── main.tf
│ └── turbopuffer.tfvars
├── azure
│ ├── main.tf
│ └── turbopuffer.tfvars
├── scripts
│ ├── apikey.py
│ ├── generate-secrets.py
│ └── sanity.sh
├── values.yaml (generated) # configuration file generated by Terraform
├── values.secret.yaml (generated) # sensitive configuration file generated by Terraform
├── compute_classes.yaml (generated, GCP only) # GKE ComputeClass manifest generated by Terraform
└── metrics-keys.yaml # configuration file provided by turbopuffer
Runlist
- Prerequisites: Check you have everything you need:
- Verify you have
terraform,kubectl, andhelminstalled. - Provision a fresh sub-account / project for your new cluster.
- Enable your cluster to pull images from our registries: Provide your GCP project number to the turbopuffer team. The kit's Terraform creates a dedicated node service account for image pulls instead of using the Compute Engine default service account, and
terraform output -raw node_service_account_emailprints its email if you prefer to allowlist only that account.
- Verify you have
- Cluster configuration: Apply the Terraform configuration to set up the Kubernetes cluster and bucket.
-
cd gcp - Run
terraform initto set up the required providers. - Fill in the required values in
turbopuffer.tfvars. Thequery_skus,index_skus, andmaintenance_machine_typevariables control which GCP machine types back the query, index, and maintenance node pools. The defaults work for most deployments. - Apply the Terraform configuration:
terraform apply -var-file=turbopuffer.tfvars - Return to the kit root:
cd ... The remaining steps run from there.
-
- Configure
kubectl: Add your new cluster context tokubectl.- Run
gcloud container clusters get-credentials CLUSTER_NAME --project PROJECT_ID --region REGION - Run
kubectl config get-contextsand confirm the context is correct. - Run
kubectl get podsand confirm the command succeeds (no output).
Apply ComputeClass manifests: Terraform writes acompute_classes.yamlat the kit root that defines GKEComputeClasspriorities across the query and index node pools. Apply it before installing Helm so the autoscaler has the priority order in place when the first pods schedule:kubectl apply -f compute_classes.yaml
- Run
- Configure Helm: The Terraform command will have generated a
values.yamlfile at the kit root, which contains values for Helm. Edit this file and set any other necessary values. Refer tovalues.schema.jsonfor a description of valid configurations.- Review the
tpuf_configvalues suggested by turbopuffer for your BYOC deployment. You can find information about these settings and more in our BYOC configuration documentation. - If you serve multiple customers from this cluster and want each one's data in its own bucket, set up a bucket per customer now. Existing data is not migrated when you add one later.
- If you terminate TLS with your cloud provider's managed certificates, follow using cloud-provider-managed TLS certificates.
- If you host images in your own registry, follow using a custom registry.
- If you deploy with ArgoCD or another GitOps tool, follow deploying with GitOps tools.
- Review the
- Generate API keys: Run
./scripts/generate-secrets.pyto generatevalues.secret.yaml. The script will generate an org ID and API key, along with a token for intra-cluster communication. - Deploy turbopuffer:
- Log in to the Helm registry: Run
helm registry login us-central1-docker.pkg.dev. Your own credentials pull the chart; the node service account only pulls images. - Install the Helm chart: Run
helm install -n default turbopuffer oci://us-central1-docker.pkg.dev/turbopuffer-onprem/charts/tpuf --values=values.yaml --values=values.secret.yaml --values=metrics-keys.yaml - For subsequent updates, run
helm upgrade -n default turbopuffer oci://us-central1-docker.pkg.dev/turbopuffer-onprem/charts/tpuf --values=values.yaml --values=values.secret.yaml --values=metrics-keys.yaml - If you render the chart and apply it with your own tooling (e.g.
helm template | kubectl apply), usekubectl apply --server-side. The chart includes a CRD too large for client-side apply's 256KB annotation limit.
- Log in to the Helm registry: Run
- Run post-deployment sanity checks:
-
TURBOPUFFER_API_KEY=<your_api_key> scripts/sanity.shwill query your turbopuffer cluster directly, verifying that core operations function. It will not verify certificates and may encounter a 500 error if the nodes aren't routable yet.
-
- Get access to monitoring: Ask the turbopuffer team for access to your cluster's Datadog dashboard. See Monitoring.
Your cluster is now running. From here, Configuration covers changing settings and Operations covers day-to-day tasks like adding organizations, scaling, and upgrades.
Using a custom registry for your turbopuffer cluster
By default, turbopuffer will pull from one of several turbopuffer-managed image registries, as configured in our included Terraform. However, there are many reasons you may want to host our images in a registry you control. Our Helm chart fully supports this through the following settings:
image.registry: YOUR_REGISTRY_URL
control_plane.image.registry: YOUR_REGISTRY_URL
We expect that registry to contain two repositories, turbopuffer and tpuf-ctl-cluster, to hold the images for turbopuffer and our control plane agent, respectively.
For customers on AWS, we can configure ECR Replication to automatically push the latest images into your registry.
Using cloud-provider-managed TLS certificates
Our Helm chart supports configuring TLS termination within your cluster using either cert-manager or native Kubernetes APIs.
Your organization may already manage certificates through your cloud provider's managed certificate offering. In that case, you will need to handle TLS termination yourself.
Regardless of your cloud provider, you will want to deploy turbopuffer internally by setting:
ingress.internal: true
GCP
To get started, set the following in values.yaml and rerun helm upgrade ... as described in step 6.
certificates.mode: disabled
Adding Google Managed Certificates to your GKE cluster is as simple as deploying the following Kubernetes manifest alongside your turbopuffer Helm deployment.
All that is required is to insert the correct value for YOUR_DOMAIN.
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
name: turbopuffer-router-config
namespace: turbopuffer
spec:
healthCheck:
checkIntervalSec: 10
timeoutSec: 10
port: 8080
type: HTTP
requestPath: /healthz
---
apiVersion: v1
kind: Service
metadata:
name: turbopuffer-router-backend
namespace: turbopuffer
annotations:
cloud.google.com/backend-config: '{"default": "turbopuffer-router-config"}'
spec:
ports:
- appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: router-http
- appProtocol: https
name: https
port: 443
protocol: TCP
targetPort: router-https
selector:
turbopuffer-routing-group: router
type: ClusterIP
---
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: managed-cert
namespace: turbopuffer
spec:
domains:
- YOUR_DOMAIN
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: turbopuffer-router-ingress
namespace: turbopuffer
annotations:
kubernetes.io/ingress.class: "gce"
networking.gke.io/managed-certificates: managed-cert
spec:
ingressClassName: "gce"
defaultBackend:
service:
name: turbopuffer-router-backend
port:
number: 80
Restricting outbound connections
If your firewall blocks outbound traffic by default, allow the following destinations:
- Polar Signals (CPU and Heap profiling)
35.234.93.182(api.polarsignals.com)
- Control Plane (Cluster Heartbeats)
34.8.199.114/32
- Datadog (Telemetry)
curl -s https://ip-ranges.us5.datadoghq.com/ | jq -r '(.agents.prefixes_ipv4 + .api.prefixes_ipv4 + .apm.prefixes_ipv4 + .global.prefixes_ipv4 + .logs.prefixes_ipv4 + .orchestrator.prefixes_ipv4 + .process.prefixes_ipv4 + .["remote-configuration"].prefixes_ipv4) | unique[]'- Note: turbopuffer reports to the
us5Datadog site, so the ranges must come fromip-ranges.us5.datadoghq.com. These ranges can change over time; if your firewall supports FQDN rules, allowing*.us5.datadoghq.comon 443 is more durable.
- Teleport (Managed BYOC only, SSH access to the jump host)
- Ask the turbopuffer team for the address.
Deploying with GitOps tools
By default, our Helm chart uses the lookup function to retain the currently running version across deployments, so a helm upgrade preserves whatever image the control plane last rolled out. Tools that render the chart without lookup (e.g. ArgoCD, Terraform, or other GitOps/CI tools) reset the image to your Helm values on each sync and undo the control plane's upgrades.
There are two ways to handle this. With ArgoCD, you can keep automated upgrades by deferring the image to the control plane. With any tool, you can roll out upgrades from your own pipeline instead.
Keeping automated upgrades under ArgoCD
ArgoCD renders charts with helm template, which doesn't support lookup (it always returns nil), so on each sync ArgoCD would otherwise reset the image to your Helm values and undo the control plane's upgrade. To keep automated upgrades working under ArgoCD, tell ArgoCD to defer the container image to the control plane.
The control plane updates the image with a server-side apply under the field manager turbopuffer-controller. By telling ArgoCD to respect that ownership, self-heal leaves the image in place while continuing to reconcile everything else from your Helm values.
Set this on your ArgoCD Application:
spec:
ignoreDifferences:
- group: apps
kind: StatefulSet
managedFieldsManagers:
- turbopuffer-controller
- group: apps
kind: Deployment
managedFieldsManagers:
- turbopuffer-controller
syncPolicy:
syncOptions:
- ServerSideApply=true # let our field manager and ArgoCD's coexist on the object
- RespectIgnoreDifferences=true # honor the rule during sync, not just in the diff view
Both sync options are required. managedFieldsManagers scopes the rule to the fields the control plane manages on those objects (the image and its version metadata), so ArgoCD keeps reconciling the rest of each object from your Helm values. ServerSideApply=true lets the control plane's field manager and ArgoCD's apply to the same object without conflict, and RespectIgnoreDifferences=true makes sync honor the rule rather than only hiding it from the diff view. Without RespectIgnoreDifferences=true, ArgoCD still reverts the image on self-heal even though the diff appears clean.
Configuring external execution for upgrades
In external_execution mode, the turbopuffer control plane will not perform upgrades directly. Instead, when a new version is available, you'll receive an automated notification to your Slack channel with the version to deploy. You can then update the turbopuffer.initial_tag value in your Helm values and apply it through your deployment pipeline.
To enable external execution for upgrades, you can set the following in your Helm values.yaml:
control_plane:
additional_config:
approvals:
upgrade: external_execution
turbopuffer:
image:
initial_tag: <commit-sha>
force_initial_tag: true
Retrieving the target version programmatically
If you want to automate this process, we expose the target version via an API endpoint that you can query from your deployment tooling:
curl -H "Authorization: Bearer <API_KEY>" \
-H "Accept: application/json" \
"https://control.turbopuffer.com/api/clusters/<cluster-id>/target_version"
Replace <API_KEY> with your cluster's API key and <cluster-id> with your cluster identifier.
The response includes both the git commit SHA and the image digest:
{
"target_version": "<commit-sha>",
"target_digest": "sha256:<digest>"
}
You can use this endpoint in your Terraform configurations, CI/CD pipelines, or any other automation to retrieve and apply the latest target version. For example, in Terraform:
data "http" "turbopuffer_target_version" {
url = "https://control.turbopuffer.com/api/clusters/${var.cluster_id}/target_version"
request_headers = {
Accept = "application/json"
Authorization = "Bearer ${var.turbopuffer_api_key}"
}
}
locals {
turbopuffer_version = jsondecode(data.http.turbopuffer_target_version.response_body)
}
# Use in your Helm release configuration:
# local.turbopuffer_version.target_version -> git commit SHA
# local.turbopuffer_version.target_digest -> image digest (sha256:...)
Creating additional clusters
You can create additional clusters by reusing your existing BYOC kit, metrics-keys.yaml, and control plane API key. Follow this runlist again from a copy of the kit directory so the existing cluster's Terraform state is untouched.
- Set a unique
cluster_name,bucket_name, andcluster_endpointinturbopuffer.tfvars. - Generate new turbopuffer org IDs and API keys for the new cluster with
scripts/generate-secrets.py. - For AWS, set
enable_spot_service_linked_role = falsewhen deploying into the same account. The role already exists and a secondterraform applyfails trying to create it.