Provisioning ibl.ai on Google Cloud (GCP)
Mirrored from
iblai/infra-cliยทdocs/GCP.md. This page is generated โ edit it in the repository, not here.
This guide walks you through standing up a single-server ibl.ai deployment on GCP, start to finish. If you've used the AWS flow, this is the same experience โ just point it at a GCP project.
Scope: single-server only. Multi-server and call-server remain AWS-only for now. Storage note: GCP creates no object storage. The platform keeps using AWS S3 โ you supply AWS credentials at the setup step (details in Storage).
What you'll end up with
Internet
โ
โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ Global external HTTPS LB โ โ one static IP, Google-managed TLS cert
โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
โ HTTP :80
โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ 1 VM (Ubuntu 22.04, Docker) โ โ external IP for SSH (locked to your IP)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
VPC ยท 1 subnet ยท 2 firewall rules ยท Cloud DNS A-records โ the LB IP
1. Prerequisites (one-time)
| # | What | How |
|---|---|---|
| 1 | A GCP project with billing on | You'll pass its project ID (e.g. my-proj-123456, not the display name). |
| 2 | Local tools | terraform (โฅ1.0) and gcloud installed. Plus this CLI: uv sync (add --extra gcp โ see step 4). |
| 3 | Authentication | Pick one: |
โข Local: gcloud auth application-default login | ||
| โข CI / automation: a service-account key JSON file | ||
| 4 | GCP Python libraries | uv sync --extra gcp (installs the Google SDKs this CLI needs). |
| 5 | IAM roles + APIs | Run iblai infra permissions --provider gcp to print them. Short version below. |
| 6 | A domain + Cloud DNS zone (for HTTPS) | Either already have a Cloud DNS zone for your domain, or let the wizard create one and you delegate it (see DNS). Skip this only if you want HTTP-only. |
| 7 | Three AWS S3 buckets + AWS keys | The platform stores media/static/backups on S3. See Storage. |
Roles + APIs (what permissions prints):
# Grant to your user or service account:
# roles/compute.admin roles/dns.admin roles/iam.serviceAccountUser
# Enable the APIs:
gcloud services enable compute.googleapis.com dns.googleapis.com --project <PROJECT_ID>
Authentication โ pick one
Option A โ your Google account (easiest for local use):
gcloud auth application-default login
gcloud auth loginalone is not enough โ Terraform and the CLI read Application Default Credentials, which only the command above writes.
Option B โ service-account key (for CI, or when your Workspace org blocks Option A's browser flow with an "Access blocked / admin needs to review" page):
PROJECT=<your-project-id>
gcloud iam service-accounts create infra-cli --project=$PROJECT
for R in roles/compute.admin roles/dns.admin roles/iam.serviceAccountUser; do
gcloud projects add-iam-policy-binding $PROJECT \
--member="serviceAccount:infra-cli@$PROJECT.iam.gserviceaccount.com" --role=$R --condition=None
done
gcloud iam service-accounts keys create ~/infra-cli-key.json \
--iam-account="infra-cli@$PROJECT.iam.gserviceaccount.com"
Use it by choosing "Service-account key file" in the wizard, or setting
GCP_CREDENTIALS_FILE=~/infra-cli-key.json in your .env. Keep the key file
out of git; delete the service account when you no longer need it.
Verify you're ready (works with either option):
iblai infra permissions --provider gcp --check --project <PROJECT_ID>
2. Provision โ the easy way (interactive)
iblai infra provision
- Choose cloud โ select GCP.
- Authentication โ project ID, region/zone, and ADC or a key file. (Validated on the spot.)
- Project & compute โ a name, environment, machine type (default
e2-standard-8), disk. - Network & access โ it auto-detects your public IP and locks SSH to it.
- Domain & certificates โ pick your Cloud DNS zone (or create one), or skip HTTPS.
- Review โ confirm, and it provisions.
When it finishes you'll see the VM IP, the app URL, and an SSH command.
AI heads-up:
e2-standard-8has 32 GB RAM. If you'll enable AI features at setup, choose a 64 GB machine (e2-standard-16orn2-highmem-8).
3. Provision โ the automated way (non-interactive)
Best for CI or repeatable runs.
cp .env.provision.gcp.example .env
# edit .env โ set PROVIDER=gcp, GCP_PROJECT_ID, DOMAIN, etc.
iblai infra provision-env -f .env
Minimum .env:
PROVIDER=gcp
GCP_PROJECT_ID=my-proj-123456
# GCP_CREDENTIALS_FILE=/path/to/key.json # omit to use `gcloud` ADC
PROJECT_NAME=mydeploy
DOMAIN=platform.example.com
VPN_IP=auto # your current IP; or a literal IP
CERT_METHOD=auto # managed cert if a Cloud DNS zone matches, else HTTP-only
DNS_ZONE_NAME=my-zone # the zone's resource name (managed cert)
See .env.provision.gcp.example for every option (machine type, disk, upload certs, create-zone, etc.).
4. DNS & HTTPS
The certificate is a classic Google-managed SSL cert covering your base domain
- 19 ibl.ai subdomains. Two things to know:
- It validates asynchronously.
applyfinishes while the cert is stillPROVISIONING. HTTPS goes live 10โ60 minutes later, once DNS resolves to the load balancer. This is normal โ the CLI tells you so. - Your domain must be delegated to Cloud DNS. If you let the CLI create the
zone (
CREATE_DNS_ZONE=trueor the wizard's "create" option), it prints the nameservers โ set those at your registrar. The cert can't validate until delegation is live.
Check cert status any time:
gcloud compute ssl-certificates describe <project>-<env>-cert --global \
--format="value(managed.status)" # PROVISIONING โ ACTIVE when ready
No domain yet? Choose CERT_METHOD=none (or "Skip HTTPS" in the wizard) to get an
HTTP-only load balancer, and add TLS later.
5. Storage uses AWS S3
GCP provisions no buckets. Before (or during) setup, make sure you have three S3 buckets in an AWS account, named on the standard convention:
<project>-<environment>-<domain-with-dots-as-dashes>-backups
<project>-<environment>-<domain-with-dots-as-dashes>-dm-media
<project>-<environment>-<domain-with-dots-as-dashes>-dm-static # public-read
Example, for PROJECT_NAME=mydeploy, ENVIRONMENT=prod, DOMAIN=platform.example.com:
mydeploy-prod-platform-example-com-backups
mydeploy-prod-platform-example-com-dm-media
mydeploy-prod-platform-example-com-dm-static (public-read)
You provide the AWS access key + secret at the setup step (below); the app on
the GCP box uses them to reach S3 โ exactly as the AWS deployment does today.
6. Set up the platform
Provisioning creates the infrastructure; setup installs and configures ibl.ai on the VM (over SSH, via Ansible):
iblai infra setup <PROJECT_NAME>
It reads the VM's IP and SSH key from the saved state and prompts for the AWS credentials (for S3), a GitHub token, and admin details. This step is identical to the AWS flow.
7. Inspect and tear down
iblai infra list # all environments (GCP rows are marked "gcp ยท single")
iblai infra status <PROJECT_NAME> # details for one
iblai infra destroy <PROJECT_NAME>
destroy removes everything this tool created (VM, LB, firewall, DNS records, cert).
A Cloud DNS zone you created outside the tool is left alone; a zone the tool
created is removed.
Troubleshooting
| Symptom | Cause & fix |
|---|---|
No Application Default Credentials found | Run gcloud auth application-default login (gcloud auth login alone is not enough), or set GCP_CREDENTIALS_FILE to a service-account key. |
| "Access blocked: โฆ admin needs to review" during ADC login | Your Workspace org blocks the ADC browser flow. Use a service-account key instead โ Authentication, Option B. |
API not enabled for this project | gcloud services enable compute.googleapis.com dns.googleapis.com --project <ID>. |
| HTTPS not working right after apply | Expected โ the managed cert takes 10โ60 min. Confirm DNS is delegated and A-records resolve to the LB IP. |
Cert stuck in PROVISIONING / FAILED_NOT_VISIBLE | A domain isn't resolving to the LB. Check registrar delegation and that every A-record points at the LB IP. |
| 503 "no healthy upstream" | Expected before iblai infra setup completes โ the LB health check probes the LMS heartbeat (learn.<domain>/heartbeat on :80), which only exists once the platform is installed. The backend flips healthy ~1โ2 min after setup finishes. If it persists: SSH in and check curl -H "Host: learn.<domain>" http://localhost/heartbeat returns 200, then gcloud compute backend-services get-health <project>-<env>-backend --global. |
apply fails assigning an external IP, or SSH keys ignored | Your project enforces an org policy (no external IPs, or OS Login). This build targets loose projects (external IP + metadata SSH). Tell the maintainers โ IAP + OS Login is a documented alternative. |
| Can't SSH to the VM | SSH is locked to the IP you provided (VPN_IP). If your IP changed, re-provision or update the firewall rule. |
How it maps to AWS (for reference)
| AWS | GCP |
|---|---|
| VPC + 2 AZ subnets | VPC + 1 regional subnet |
| Security groups | Firewall rules (by network tag) |
| EC2 + key pair | Compute instance + metadata SSH key |
| Application Load Balancer | Global external ALB (EXTERNAL_MANAGED) |
| Target group | Unmanaged instance group + backend service |
| ACM cert (Route53-validated) | Google-managed cert (Cloud DNS, async) |
| Route53 A-alias records | Cloud DNS A-records โ static LB IP |
| S3 buckets | (none โ reuses AWS S3) |
Deeper internals live in CLAUDE.md under GCP Provider (single-server).