How to Add a New API
Adding a new Cloud Run service behind the gateway requires changes in up to four repos. Work through these steps in order.
Step 1 — Create the Cloud Run service
In iac-access (or a new dedicated IaC repo):
- Deploy the service to
europe-west1for co-location with the gateway VM. - Grant
roles/run.invokerto the gateway service account:
resource "google_cloud_run_v2_service_iam_member" "gateway_invoker" {
project = var.iac_project
location = var.location
name = google_cloud_run_v2_service.my_new_service.name
role = "roles/run.invoker"
member = "serviceAccount:cf-tunnel-gateway@${var.iac_project}.iam.gserviceaccount.com"
}
- Set Cloud Run ingress to
INTERNAL_AND_CLOUD_LOAD_BALANCING(orINTERNAL) — the service must not be publicly reachable.
Apply this change first so the Cloud Run URI is available before the gateway references it.
Step 2 — Add the route in cf-proxy
In iac-api-gateway/gateway/:
main.tf — add a data source to read the new service's URI:
data "google_cloud_run_v2_service" "my_new_service" {
name = "svc-my-new-service"
location = var.location
project = var.iac_project
}
vm.tf — add the hostname→URL mapping to routes_json:
locals {
routes_json = jsonencode({
(local.cf_admin_internal_domain) = data.google_cloud_run_v2_service.admin.uri
(local.cf_public_internal_domain) = data.google_cloud_run_v2_service.public.uri
# Add your new entry:
"my-new-api-internal-${var.iac_environment}.${var.cf_zone_name}" = data.google_cloud_run_v2_service.my_new_service.uri
})
}
Changing
routes_jsonupdates the startup script. Terraform will create a new instance template, and the MIG's PROACTIVE update policy will replace the VM automatically onapply.
Step 3 — Add a tunnel ingress rule
In iac-api-gateway/gateway/tunnel.tf, add a new rule before the catch-all:
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "gateway" {
# ...
config = {
ingress = [
{ hostname = local.cf_admin_internal_domain, service = "http://localhost:8080" },
{ hostname = local.cf_public_internal_domain, service = "http://localhost:8080" },
# Add your new rule before the catch-all:
{
hostname = "my-new-api-internal-${var.iac_environment}.${var.cf_zone_name}"
service = "http://localhost:8080"
},
{ service = "http_status:404" }, # catch-all — must stay last
]
}
}
Ingress rule changes are live-updated to cloudflared without a VM restart (because config_src = "cloudflare").
Step 4 — Add a DNS record
In iac-api-gateway/gateway/dns.tf:
resource "cloudflare_dns_record" "my_new_api_internal" {
count = var.enable_dns_switch ? 1 : 0
zone_id = var.cf_zone_id
name = "my-new-api-internal-${var.iac_environment}"
type = "CNAME"
content = "${cloudflare_zero_trust_tunnel_cloudflared.gateway.id}.cfargotunnel.com"
ttl = 1
proxied = true
comment = "Managed by Terraform (iac-api-gateway/gateway) — my new API origin"
}
Step 5 — Create or update the Worker
In platform-api-gateway/workers/:
Either create a new Worker directory (copy an existing one as a template) or add a route to an existing Worker.
Required wrangler.toml elements:
[env.dev.vars]
ORIGIN_URL = "https://my-new-api-internal-dev.eigenoid.services"
ALLOWED_ORIGIN = "https://my-portal-dev.eigenoid.com" # adjust per audience
[[env.dev.secrets_store_secrets]]
binding = "INTERNAL_AUTH_TOKEN"
store_id = "e30a2aceebe34a80a10da2cc6bb208c4"
secret_name = "gateway-dev-internal-auth-token"
Required Worker behavior:
- Strip
x-internal-auth-tokenfrom incoming client requests (prevent spoofing). - Resolve
INTERNAL_AUTH_TOKENviaawait env.INTERNAL_AUTH_TOKEN.get()— it is aSecretsStoreSecret, not a plain string. - Set
X-Internal-Auth-Token: <token>on the proxied request. - Proxy only to the fixed
ORIGIN_URL— never use a client-supplied URL.
Step 6 — Apply changes
Changes in iac-api-gateway are deployed through the standard Terraflow pipeline:
1. Open a PR in iac-api-gateway
→ Automatic terraform plan (dev)
→ Review the plan output in the PR comment
2. Comment /terraflow apply on the PR
→ Applies to dev
3. Merge the PR
4. Promote to qa/prd via workflow_dispatch
Changes in platform-api-gateway are deployed by Workers Builds CI on push to the dev branch.
Checklist
□ Cloud Run service deployed in europe-west1
□ roles/run.invoker granted to cf-tunnel-gateway@{project}.iam.gserviceaccount.com
□ Cloud Run ingress set to INTERNAL or INTERNAL_AND_CLOUD_LOAD_BALANCING
□ data source added in main.tf
□ Hostname→URL entry added to routes_json in vm.tf
□ Ingress rule added to tunnel.tf (before catch-all)
□ CNAME record added to dns.tf
□ Worker created or updated with correct ORIGIN_URL and secrets_store_secrets binding
□ /terraflow apply run on iac-api-gateway PR (dev)
□ Worker deployed (push to dev branch)
□ Smoke test: curl through the Worker, check cf-proxy logs
Related pages
- Cloudflare Workers — Worker configuration details
- Cloudflare Tunnel — tunnel ingress architecture
- Gateway VM — cf-proxy routing logic
- Cloud Run services — IAM and ingress requirements