> ## Documentation Index
> Fetch the complete documentation index at: https://firebolt-aggregate-helm-docs-pr-79.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Google Cloud Storage object storage for engine managed table data, with GKE Workload Identity Federation and intermediary service accounts for external access.

# Google Cloud Storage

This page configures Google Cloud Storage as engine object storage.

Every engine needs object storage for managed table data. The chart does not support local-filesystem storage for engines, so an engine pod never becomes Ready until `customEngineConfig.storage` points at object storage.

With Google Cloud Storage as the backing store, durability does not depend on the per-pod data volumes mounted to each engine. Even a complete loss of those volumes does not cause data loss, because the authoritative copy of managed table data lives in the bucket.

You configure object storage on the engine through `customEngineConfig.storage`, which the chart passes through unchanged into the engine's `config.yaml`. The `managed_table_storage` and `managed_table_bucket_name` keys match the Firebolt Core configuration schema, and the chart does not validate them. The engine reads Google Cloud credentials from the pod's Google identity, which you provide with [Workload Identity Federation for GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity).

<Note>
  The chart passes `customEngineConfig.storage` through unchanged and does not validate `managed_table_storage`. The `gcs` backend requires an engine image that supports it. An unsupported value is written verbatim into the engine `config.yaml`, so the engine fails at startup rather than at install time.
</Note>

## Prerequisites

Before you begin, ensure that you have the following installed and configured:

* A Kubernetes cluster running on Google Kubernetes Engine with Workload Identity Federation enabled.
* `kubectl` configured to access your cluster.
* `helm` v3 installed on your local machine.
* `gcloud` configured for your project.
* A Google Cloud project with permissions to create buckets and IAM service accounts.
* An engine image that supports the `gcs` storage backend.

## Use Google Cloud Storage

The following examples use a bucket named `firebolt-managed` in the project `my-project`, but you can choose any name you like.

### Create a bucket

Create a Google Cloud Storage bucket with uniform bucket-level access and public access prevention:

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Project, location, and bucket name used by the gcloud calls below.
export GCP_PROJECT=my-project
export GCP_LOCATION=us-east4
export BUCKET_NAME=firebolt-managed

# Create the bucket.
gcloud storage buckets create "gs://${BUCKET_NAME}" \
  --project="${GCP_PROJECT}" \
  --location="${GCP_LOCATION}" \
  --uniform-bucket-level-access \
  --public-access-prevention
```

### Grant the engine a Google identity

Create a Google service account, grant it object access on the bucket, and allow the engine's Kubernetes ServiceAccount to impersonate it:

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Identity names used by the gcloud calls below.
export GSA_NAME=firebolt-engine
export GSA_EMAIL="${GSA_NAME}@${GCP_PROJECT}.iam.gserviceaccount.com"
export K8S_NAMESPACE=firebolt
export K8S_SA=firebolt-engine

# Create the Google service account for the engine.
gcloud iam service-accounts create "${GSA_NAME}" \
  --project="${GCP_PROJECT}"

# Grant the service account object read and write access on the bucket.
gcloud storage buckets add-iam-policy-binding "gs://${BUCKET_NAME}" \
  --member="serviceAccount:${GSA_EMAIL}" \
  --role="roles/storage.objectAdmin"

# Allow the Kubernetes ServiceAccount to impersonate the Google service account.
gcloud iam service-accounts add-iam-policy-binding "${GSA_EMAIL}" \
  --project="${GCP_PROJECT}" \
  --role="roles/iam.workloadIdentityUser" \
  --member="serviceAccount:${GCP_PROJECT}.svc.id.goog[${K8S_NAMESPACE}/${K8S_SA}]"
```

Annotate the Kubernetes ServiceAccount with the Google service account so GKE injects credentials into engine pods that run under it:

```yaml theme={"theme":{"light":"css-variables","dark":"css-variables"}}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: firebolt-engine
  namespace: firebolt
  annotations:
    iam.gke.io/gcp-service-account: firebolt-engine@my-project.iam.gserviceaccount.com
```

### Point the chart at the bucket

Run the engine pods under the annotated ServiceAccount and set the storage block to the Google Cloud Storage bucket. The default scheme for `gcs` is `gs://`.

```yaml theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# my-values.yaml
engineSpec:
  serviceAccount: firebolt-engine

customEngineConfig:
  storage:
    managed_table_storage: gcs
    managed_table_bucket_name: firebolt-managed
```

Create the ServiceAccount, then install the chart with the matching values:

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Create the Workload-Identity-annotated ServiceAccount in the release namespace.
kubectl apply -f engine-serviceaccount.yaml

# Install the chart against the bucket and the ServiceAccount.
helm install firebolt ./helm \
  --namespace firebolt --create-namespace \
  -f my-values.yaml
```

### Confirm that object storage works

Create a table, insert a row, and list the bucket to confirm the engine wrote data through to Google Cloud Storage:

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Forward the gateway Service to localhost:8080 in the background.
kubectl -n firebolt port-forward svc/firebolt-gateway 8080:80 &

# Create a table on the engine.
curl -s http://localhost:8080/ -H "X-Firebolt-Engine: default" \
  -H "Content-Type: text/plain" --data "create table t (val int)"

# Insert one row, which forces the engine to write a tablet.
curl -s http://localhost:8080/ -H "X-Firebolt-Engine: default" \
  -H "Content-Type: text/plain" --data "insert into t values (1)"

# List the bucket. New object-storage prefixes appear as the engine writes data.
gcloud storage ls "gs://firebolt-managed"
```

New prefixes appear under the bucket as the engine writes data.

## Restrict external access with an intermediary service account

The bucket you set under `customEngineConfig.storage` holds the engine's managed tablet data, and the engine reaches it with the engine pod's own Google identity. Queries that read from or write to external locations, such as external tables that point at a different bucket, follow a separate credential path.

By default, external access also uses the engine pod's own Google identity. That identity belongs to this chart release, so it is not a convenient identity for the owner of an external bucket to reference when they grant access.

An intermediary service account gives external access a stable identity instead. When you set one, the engine impersonates the intermediary service account for external access rather than using its own pod identity. Because the service account is stable and known ahead of time, you can share it with third parties and reference it in bucket IAM policies, including on Google Cloud projects outside your own organization. Access to the object storage bucket always uses the engine pod's own identity, so the intermediary service account applies only to external locations.

A `gs://` location that carries `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` (a Google Cloud Storage HMAC key) is the exception: the query authenticates with that key through the S3-compatible XML API, bypassing both the pod identity and the intermediary service account.

Where nothing provides Application Default Credentials to the engine pods, a credential-less `gs://` read is sent unauthenticated and reaches publicly readable objects only, and writing needs an identity and fails. That is the case on a cluster outside Google Cloud, and on GKE without Workload Identity and without `GOOGLE_APPLICATION_CREDENTIALS`.

The pod identity is whatever Application Default Credentials resolve to inside the engine container. Google's libraries look for them in a fixed order, and the first match wins:

1. `GOOGLE_APPLICATION_CREDENTIALS`, if it is set. The file it names is used as given, whether that is a mounted service account key or a credential configuration file for Workload Identity Federation.
2. `$HOME/.config/gcloud/application_default_credentials.json`, if that file exists. This is where `gcloud auth application-default login` writes, so it is normally present only on a workstation.
3. The instance metadata server, which is what Workload Identity Federation for GKE provides.

Step 2 takes precedence over step 3, so an engine whose `HOME` holds a `gcloud` login authenticates as that person instead of as the pod identity. To pin the source, set `GOOGLE_APPLICATION_CREDENTIALS` on the engine container, or set `HOME` to a directory that only the engine writes to. The engine logs the source it resolved once per process, as `Using Google Cloud identity from <source>`.

You can also keep external access off the pod identity altogether, with `customEngineConfig.storage.gcp.allow_engine_identity: false`. A credential-less `gs://` read is then sent unauthenticated and reaches publicly readable objects only, and a write fails with an error that asks for credentials, so an external `gs://` location needs either an HMAC key or an intermediary service account. The chart renders `true` for the engines it installs, because the pod identity belongs to this chart release and is your own; the engine itself refuses that identity until a deployment permits it, because it cannot tell whose identity it runs as. When an intermediary service account is set, that account is the principal, so the setting has no effect.

Because a credential-less external location runs as the engine pod's identity, plan the identity's bucket access as the boundary. The engine rejects a credential-less URL that names the bucket in `managed_table_bucket_name`, so managed tablet data is not readable or writable that way. To use that bucket as an external location anyway, supply `CREDENTIALS` on the location: the query then authenticates as that principal rather than as the engine.

Every other bucket the pod identity can reach stays reachable by URL. An inline `gs://` URL carries no location privileges, so any user who can run a query can read, overwrite, or delete objects in those buckets. Grant the pod identity object access only on the managed bucket, and set an intermediary service account for external data so the pod identity itself needs no access to it.

Create the intermediary Google service account, grant the engine's identity `roles/iam.serviceAccountTokenCreator` on it, and grant the intermediary the permissions it needs to reach the external data.

Set its ID under `customEngineConfig.storage.gcp.intermediary_service_account_id`:

```yaml theme={"theme":{"light":"css-variables","dark":"css-variables"}}
customEngineConfig:
  storage:
    managed_table_storage: gcs
    managed_table_bucket_name: firebolt-managed
    gcp:
      intermediary_service_account_id: projects/my-project/serviceAccounts/firebolt-intermediary@my-project.iam.gserviceaccount.com
```

The chart passes the `storage.gcp` block through unchanged, except for `allow_engine_identity`, which it renders as `true` unless you set it. The connection settings in the block apply to managed tables when `managed_table_storage` is `gcs`; `allow_engine_identity` governs external `gs://` locations, so it applies whatever backs managed tables.

## Storage scope

`customEngineConfig` is global to the release. Multiple engines under the same `engines:` list share the same `customEngineConfig.storage` block, and therefore the same bucket. To run engines against different buckets, install the chart twice in separate releases, each with its own `customEngineConfig.storage`.
