> For the complete documentation index, see [llms.txt](https://docs.zerve.ai/zerve-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zerve.ai/zerve-docs/self-hosting/gcp-gke.md).

# GCP (GKE)

Run Zerve's compute and storage in your own Google Cloud project, on your existing GKE cluster.

This guide sets up [bring your own cloud](/zerve-docs/self-hosting/self-hosting.md#bring-your-own-cloud) on Google Cloud. Your code runs on your existing **GKE cluster**, and your data is stored in your own Cloud Storage bucket.

## What you'll set up

| Resource                                    | Purpose                                                                                                                                                                                                                               |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **A Cloud Storage bucket**                  | Stores block outputs, variables and files                                                                                                                                                                                             |
| **An Artifact Registry repository**         | Stores environment images                                                                                                                                                                                                             |
| **An application service account**          | Represents Zerve in your project. Zerve uses [service account impersonation](https://cloud.google.com/iam/docs/service-account-impersonation) to get short-lived credentials, for example to schedule compute jobs or manage storage. |
| **An execution service account**            | The identity your code runs as. Grant it access to other Google Cloud resources your code needs.                                                                                                                                      |
| **A build service account**                 | The identity environment builds run as                                                                                                                                                                                                |
| **Zerve's Helm chart, on your GKE cluster** | Sets up the permissions Zerve needs in your cluster                                                                                                                                                                                   |

Your GKE cluster doesn't have to be in the same project as the other resources.

## Before you start

Your GKE cluster needs:

* Kubernetes version **1.28 or higher**
* [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) enabled
* A **DNS-based control plane endpoint**

You'll also need the `gcloud` CLI and `helm`, and permission to create service accounts, buckets and repositories in your project.

## 1. Set some variables

These are used throughout the guide:

```bash
export PROJECT_ID=        # The Google Cloud project for Zerve's resources
export ZERVE_ORG_ID=      # Your organization's ID in Zerve
export REGION=europe-west2  # For best performance, use your GKE cluster's region
export BUCKET_NAME="zerve-$ZERVE_ORG_ID"
```

Set these too, for your GKE cluster's project and node service account:

```bash
export COMPUTE_PROJECT_ID=
export COMPUTE_NODES_SA=
```

Point the `gcloud` CLI at the project for Zerve's resources:

```bash
gcloud config set project "$PROJECT_ID"
```

## 2. Create the service accounts

```bash
gcloud iam service-accounts create zerve-app-sa --display-name "Zerve application service account"
gcloud iam service-accounts create zerve-execution-sa --display-name "Zerve execution service account"
gcloud iam service-accounts create zerve-build-sa --display-name "Zerve build service account"
```

Allow Zerve to impersonate the application service account:

```bash
gcloud iam service-accounts add-iam-policy-binding "zerve-app-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --member "serviceAccount:zerve-sa@zerve-14458.iam.gserviceaccount.com" \
  --role roles/iam.serviceAccountTokenCreator
```

## 3. Create the bucket

```bash
gcloud storage buckets create "gs://$BUCKET_NAME" \
  --location "$REGION" \
  --public-access-prevention \
  --uniform-bucket-level-access \
  --enable-hierarchical-namespace
```

Allow the Zerve app to read and write files in the bucket from the browser:

```bash
gcloud storage buckets update "gs://$BUCKET_NAME" --cors-file /dev/stdin <<- EOF
    [
      {
        "origin": ["https://app.zerve.ai"],
        "responseHeader": ["*"],
        "method": ["GET", "PUT", "POST"],
        "maxAgeSeconds": 3600
      }
    ]
EOF
```

Give each service account access to the parts of the bucket it needs:

```bash
gcloud storage buckets add-iam-policy-binding "gs://$BUCKET_NAME" \
  --member "serviceAccount:zerve-app-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "roles/storage.objectAdmin"

gcloud storage managed-folders create "gs://$BUCKET_NAME/build"
gcloud storage managed-folders add-iam-policy-binding "gs://$BUCKET_NAME/build" \
  --member "serviceAccount:zerve-build-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role roles/storage.objectViewer

gcloud storage managed-folders create "gs://$BUCKET_NAME/canvases"
gcloud storage managed-folders add-iam-policy-binding "gs://$BUCKET_NAME/canvases" \
  --member "serviceAccount:zerve-execution-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role roles/storage.objectAdmin
```

## 4. Create the image repository

```bash
gcloud artifacts repositories create zerve \
  --location "$REGION" \
  --repository-format docker \
  --mode standard-repository

gcloud artifacts repositories add-iam-policy-binding zerve \
  --location "$REGION" \
  --member "serviceAccount:zerve-build-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "roles/artifactregistry.repoAdmin"
```

## 5. Set up logging

```bash
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member "serviceAccount:zerve-execution-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "roles/logging.logWriter"

gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member "serviceAccount:zerve-build-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "roles/logging.logWriter"

gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member "serviceAccount:zerve-app-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "roles/logging.viewer"
```

## 6. Connect your GKE cluster

Let the cluster's Kubernetes service accounts act as Zerve's execution and build service accounts:

```bash
gcloud iam service-accounts add-iam-policy-binding "zerve-execution-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --member "serviceAccount:$COMPUTE_PROJECT_ID.svc.id.goog[zerve/executor]" \
  --role roles/iam.workloadIdentityUser

gcloud iam service-accounts add-iam-policy-binding "zerve-build-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --member "serviceAccount:$COMPUTE_PROJECT_ID.svc.id.goog[zerve/builder]" \
  --role roles/iam.workloadIdentityUser
```

Let the cluster's nodes pull images from the repository:

```bash
gcloud artifacts repositories add-iam-policy-binding zerve \
  --location "$REGION" \
  --member "serviceAccount:$COMPUTE_NODES_SA" \
  --role "roles/artifactregistry.reader"
```

Let Zerve connect to your GKE cluster:

```bash
gcloud iam roles create ClustersConnect \
  --project "$COMPUTE_PROJECT_ID" \
  --title "Clusters connect role" \
  --description "Allow connecting to clusters" \
  --permissions "container.clusters.connect"

gcloud projects add-iam-policy-binding "$COMPUTE_PROJECT_ID" \
  --member "serviceAccount:zerve-app-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "projects/$COMPUTE_PROJECT_ID/roles/ClustersConnect"
```

Install Zerve's Helm chart, which sets up the permissions Zerve needs in the cluster:

```bash
cat <<- EOF > /tmp/values.yaml
  builder:
    # Link the "builder" Kubernetes service account to Zerve's build service account
    serviceAccount:
      annotations:
        iam.gke.io/gcp-service-account: zerve-build-sa@$PROJECT_ID.iam.gserviceaccount.com
  executor:
    # Link the "executor" Kubernetes service account to Zerve's execution service account
    serviceAccount:
      annotations:
        iam.gke.io/gcp-service-account: zerve-execution-sa@$PROJECT_ID.iam.gserviceaccount.com
  scheduler:
    # Give the Zerve app full access to the zerve namespace
    user:
      enabled: true
      name: zerve-app-sa@$PROJECT_ID.iam.gserviceaccount.com
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: admin
EOF

helm install zerve oci://public.ecr.aws/x8w6c8k3/helm/zerve -n zerve -f /tmp/values.yaml --create-namespace
```

## 7. Connect Zerve to your project

In Zerve, go to **Settings → Self-Hosting** and fill in:

| Field                 | Value                                                                                                           |
| --------------------- | --------------------------------------------------------------------------------------------------------------- |
| **Project ID**        | The project for Zerve's resources, such as `flying-banana-412312-r9`                                            |
| **Region**            | The region you used, such as `europe-west2`                                                                     |
| **Bucket Name**       | The bucket you created                                                                                          |
| **Service Account**   | The application service account's email, such as `zerve-app-sa@flying-banana-412312-r9.iam.gserviceaccount.com` |
| **Docker Repository** | The repository's address, such as `europe-west2-docker.pkg.dev/flying-banana-412312-r9/zerve`                   |

Under **Compute options**, select **Kubernetes** and fill in:

| Field                          | Value                                                                                                         |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| **Namespace**                  | `zerve`                                                                                                       |
| **Endpoint**                   | Your GKE cluster's DNS-based control plane endpoint, such as `https://gke-723981544496.europe-west2.gke.goog` |
| **Service Account Token**      | Leave empty                                                                                                   |
| **Certificate Authority Data** | Leave empty                                                                                                   |

To find your cluster's endpoint:

```bash
gcloud container clusters describe CLUSTER_NAME \
  --location "$REGION" \
  --format 'value(controlPlaneEndpointsConfig.dnsEndpointConfig.endpoint)'
```

Save the settings. Your organization's code now runs on your GKE cluster.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.zerve.ai/zerve-docs/self-hosting/gcp-gke.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
