> ## Documentation Index
> Fetch the complete documentation index at: https://6185833-opencomputer.manicule.dev/llms.txt
> Use this file to discover all available pages before exploring further.
> For every page in one file, fetch https://6185833-opencomputer.manicule.dev/llms-full.txt (large).
> To search and read these docs as tools, connect the MCP server at https://6185833-opencomputer.manicule.dev/api/mcp.
> Every agent surface: https://6185833-opencomputer.manicule.dev/.well-known/llms-manifest.json

# On Google Cloud (/self-hosting/gcp)

<!-- agent-signals: reading_time_min: 5 · est_tokens: 2108 · updated: 2026-11-13 -->
Related: [Start here](/self-hosting/overview.md), [On Azure](/self-hosting/azure.md), [On AWS](/self-hosting/aws.md), [Overview](/api/overview.md)

> Deploy the app and gateway Workers on Cloudflare, and create each organization's Computer VM on Compute Engine yourself

On Google Cloud the gateway runs with `COMPUTER_VMS` set to `self-managed`: it creates no VMs and needs no platform Worker. You create one Compute Engine instance per organization, and the instance connects out to the gateway with that organization's host token.

| The gateway                                                                                                              | You                                         |
| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- |
| Shows the Computer as `Online` while its VM is connected and `Offline` otherwise                                         | Create, size, stop, start and delete the VM |
| Holds a Slack message that arrives while the VM is offline, says so once in the thread, and runs it when the VM connects | Start the VM again                          |

The examples use `app.example.com` and `gateway.example.com`, as in [On Azure](/self-hosting/azure).

## 1. Get the code

```bash
git clone https://github.com/diggerhq/opencomputer.git
cd opencomputer
(cd services/managed-agents && npm ci)
(cd cloudflare-workers/api-edge && npm ci)
(cd web && npm ci)
```

## 2. Create the Cloudflare resources and the apps

Follow two steps of the Azure guide unchanged:

* [Create the Cloudflare resources](/self-hosting/azure#5-create-the-cloudflare-resources)
* [Create the WorkOS, Slack and GitHub apps](/self-hosting/azure#7-create-the-workos-slack-and-github-apps)

## 3. Deploy the gateway Worker

Follow [Deploy the gateway Worker](/self-hosting/azure#8-deploy-the-gateway-worker) with these differences in `wrangler.production.jsonc`:

| Field                                                                      | Value                                                |
| -------------------------------------------------------------------------- | ---------------------------------------------------- |
| `COMPUTER_VMS`                                                             | Add it, with the value `self-managed`                |
| `services`                                                                 | Remove the whole block. There is no platform Worker. |
| `COMPUTER_HOST_GATEWAY_URL`, `COMPUTER_DEFAULT_SIZE`, `COMPUTER_POOL_SIZE` | Remove. They are not used.                           |

The secrets are the same. Keep the value of `COMPUTER_HOST_TOKEN_KEY` at hand: step 6 derives each organization's host token from it.

## 4. Deploy the app Worker

Follow [Deploy the app Worker](/self-hosting/azure#9-deploy-the-app-worker) unchanged.

## 5. Build and upload the Computer release

```bash
cd computer
deploy/bundle.sh /tmp/calvin.bundle

gcloud config set project '<PROJECT_ID>'
export PROJECT="$(gcloud config get-value project)"
export REGION=us-central1
export ZONE=us-central1-a
export BUCKET=<globally unique>

gcloud storage buckets create "gs://$BUCKET" --location="$REGION" --uniform-bucket-level-access
gcloud storage cp /tmp/calvin.bundle "gs://$BUCKET/calvin.bundle"
```

The bundle holds the Computer source and the model proxy built for Linux amd64. The bucket stays private. Create a service account for the VMs that can only read the bucket:

```bash
gcloud iam service-accounts create computer-vm --display-name='Computer VMs'
export SA="computer-vm@$PROJECT.iam.gserviceaccount.com"
gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
  --member="serviceAccount:$SA" --role=roles/storage.objectViewer
```

## 6. Get the organization ID and make the host token

1. Open `https://app.example.com` and sign in.
2. Open **Settings** and copy the **Organization ID**.
3. Derive the organization's host token from `COMPUTER_HOST_TOKEN_KEY`:

```bash
export ORG_ID='<ORGANIZATION_ID>'
export COMPUTER_HOST_TOKEN_KEY='<the gateway secret>'

export TOKEN="sph_$(printf 'calvin-host:%s' "$ORG_ID" \
  | openssl dgst -sha256 -hmac "$COMPUTER_HOST_TOKEN_KEY" -binary \
  | base64 | tr '+/' '-_' | tr -d '=')"
```

The token is valid for that organization only, and for as long as the key does not change.

## 7. Create the VM

The VM is Ubuntu 24.04 on amd64 with a second, blank disk for `/data`. It needs outbound internet access and no inbound access: a new VPC network has no firewall rule that allows inbound traffic.

Write the first-boot script. It stores the gateway URL and the token in a root-only file, downloads the release with the VM's service account and runs its boot script:

```bash
cat > computer-user-data.sh <<EOF
#!/bin/bash
set -euo pipefail
umask 077
cat > /root/calvin-computer.env <<ENV
SP_GATEWAY_URL=wss://gateway.example.com/v1/computer/host?organizationId=$ORG_ID
SP_HOST_TOKEN=$TOKEN
SP_HOST_ID=$ORG_ID
DATA_DEVICE=/dev/disk/by-id/google-computer-data
ENV
umask 022
apt-get update -q
apt-get install -y -q git curl
install -d -m 0755 /opt/bootstrap
metadata=http://metadata.google.internal/computeMetadata/v1
access_token=\$(curl -fsS -H 'Metadata-Flavor: Google' "\$metadata/instance/service-accounts/default/token" \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
curl -fsSL --retry 5 -H "Authorization: Bearer \$access_token" -o /opt/bootstrap/calvin.bundle \
  'https://storage.googleapis.com/storage/v1/b/$BUCKET/o/calvin.bundle?alt=media'
rm -rf /opt/bootstrap/calvin-src
git clone -q --branch main /opt/bootstrap/calvin.bundle /opt/bootstrap/calvin-src
BOOTSTRAP_FETCH=0 bash /opt/bootstrap/calvin-src/deploy/vm/azure-computer-boot.sh
EOF
chmod 600 computer-user-data.sh
```

Create the network and the instance. cloud-init runs the script in the `user-data` metadata key once, on the first boot:

```bash
gcloud compute networks create computer --subnet-mode=auto

gcloud compute instances create computer-1 --zone="$ZONE" \
  --machine-type=e2-standard-2 \
  --network=computer \
  --image-family=ubuntu-2404-lts-amd64 --image-project=ubuntu-os-cloud \
  --boot-disk-size=64GB \
  --create-disk=name=computer-1-data,device-name=computer-data,size=128GB,type=pd-balanced,auto-delete=no \
  --service-account="$SA" --scopes=storage-ro \
  --metadata-from-file=user-data=computer-user-data.sh
```

The first boot installs the toolchains, formats the blank disk, mounts it at `/data` and starts the host. It takes about five minutes. The boot script formats a disk only when it is blank.

The token stays in the instance's metadata. On the VM only root can read it; in Google Cloud, anyone allowed to read the instance (`compute.instances.get`) can.

## 8. Add the Slack app and connect GitHub

1. Open `https://app.example.com`. The Computer shows `Online` once the VM has connected.
2. Click **Add to Slack** and approve.
3. Connect GitHub and choose the repositories.
4. Mention `@computer` in a channel.

Repeat steps 6 and 7 for each further organization, with another instance and disk name.

## Stop, start and resize

Stop the instance with `gcloud compute instances stop`; the Computer shows `Offline` and Slack messages wait. Start it with `gcloud compute instances start`; the host reconnects and the workspace is still on `/data`. To resize, stop the instance, change its type with `gcloud compute instances set-machine-type`, and start it.

Deleting the instance does not delete the data disk.

## Check a VM

The boot log is `/var/log/calvin-boot.log`, and the two services are `slack-presence-host` and `oc-modelproxy`. No firewall rule allows SSH. To get a shell, allow SSH from Identity-Aware Proxy only, then connect through it:

```bash
gcloud compute firewall-rules create computer-iap-ssh --network=computer \
  --allow=tcp:22 --source-ranges=35.235.240.0/20

gcloud compute ssh computer-1 --zone="$ZONE" --tunnel-through-iap --command \
  'sudo systemctl is-active slack-presence-host.service oc-modelproxy.service; sudo tail -50 /var/log/calvin-boot.log'
```

## Update a VM

A self-managed VM does not update itself. Build and upload a new bundle (step 5), download it to `/opt/bootstrap/calvin.bundle` on the VM, and run as root:

```bash
git -C /opt/bootstrap/calvin-src fetch -q /opt/bootstrap/calvin.bundle main
git -C /opt/bootstrap/calvin-src checkout -q -f --detach FETCH_HEAD
CODE_ONLY=1 BOOTSTRAP_FETCH=0 bash /opt/bootstrap/calvin-src/deploy/vm/azure-computer-boot.sh
```
