Gateway installation guide for GCP
This guide installs the Agent Router gateway in a Google Cloud project using the tare command-line utility. The resulting ingress terminates TLS for the data plane and routes traffic to it from a customer-facing hostname.
Plan for 45--90 minutes end-to-end:
- 10–15 minutes of CLI work.
- 15–60 minutes of background certificate provisioning.
Architecture
┌───────────────────────────────────────────┐
│ proxy.<your-domain> ← customer DNS │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Static IP │ ← GCP global address │
│ └────────┬───────┘ │
│ │ │
│ ┌────────▼───────┐ │
│ │ Cert Map + │ ← TLS termination │
│ │ Managed Cert │ │
│ └────────┬───────┘ │
│ │ │
│ ┌────────▼───────┐ │
│ │ Agent Router | ← installed in this │
│ │ Gateway(k8s) │ guide │
│ └────────┬───────┘ │
│ │ │
│ ┌────────▼───────┐ │
│ │ Agent Router | |
| | Dataplane │ ← installed earlier │
│ └────────────────┘ via tare CLI │
└───────────────────────────────────────────┘
The tare utility provisions everything from the static IP down to the gateway routes. The operator provides one customer-facing DNS record at the top.
Prerequisites
The following are required before starting:
- Access to a GCP project with at least the following roles:
roles/compute.networkAdmin(static IP).roles/certificatemanager.editor(cert map, cert, DNS auth).
- Access to the Kubernetes cluster where the Agent Router dataplane runs.
kubectl cluster-infomust succeed against the target cluster. - Control over the DNS provider for the domain in use (for example Cloudflare, Route 53, or an internal DNS team). Two records are created during the install.
- The Agent Router dataplane already installed. This guide assumes
tare installhas already run successfully against the target cluster. gcloudinstalled and authenticated when using--apply-prereqsto let the CLI provision the GCP resources. When the platform team manages those via Terraform or another IaC tool, skipgcloudand use--ack-prereqsinstead.- Two files from Tetrate:
- The identity file (
identity.json), issued by the management plane during onboarding. - A starter
gcp-gateway.json. A Tetrate field engineer typically provides a template tuned to the target environment.
- The identity file (
The tare binary ships helm and kubectl embedded; no separate install is required.
Step 1: prepare the config
The gateway install is driven by a single JSON contract: gcp-gateway.json. Every value can also be passed as a CLI flag, but the config file is the recommended unit for version control and review.
A complete example:
{
"projectId": "withfraser",
"serveDomain": "proxy.acme.tetrate.ai",
"customer": "acme",
"environment": "production",
"serveUrl": "proxy.acme.tetrate.ai",
"certificateMap": {
"name": "acme-tetrate-ai"
},
"certificate": {
"name": "acme-serve-cert"
},
"dnsAuthorization": {
"name": "proxy-acme-tetrate-ai-dns-auth"
},
"namespaces": {
"gateway": "tars-gateway",
"system": "tars-system",
"dataplane": "tars-dataplane"
},
"gcloud": {
"skipExisting": true
}
}
Commonly edited fields:
| Field | Description |
|---|---|
projectId | The GCP project that hosts the static IP, cert, and cert map. |
serveDomain | The fully-qualified hostname customers will use. Single host only; wildcards are not yet supported. |
customer | The customer identifier, matching the value in the identity file. |
environment | Free-form label propagated to Helm values. Typical values: production, staging. |
certificateMap.name | Name for the GCP certificate map. Convention: <customer>-<domain-suffix>. |
certificate.name | Name for the managed certificate. Convention: <customer>-serve-cert. |
gcloud.skipExisting | When true, re-runs are idempotent; re-installs do not fail when a resource already exists. Recommended true. |
For the full schema, including optional fields such as gateway.staticIpName and securityPolicy, see the gcp-gateway.json schema reference.
Set gateway.staticIpName to pin the gateway to a reserved static IP. Without it, the gateway receives an ephemeral GCP address that may change on reschedule or upgrade, silently breaking the DNS A record from Step 5.
Step 2: validate the config
Lint the config before any cloud or cluster operation:
tare gateway config lint --config gcp-gateway.json
The check is hermetic (no GCP or Kubernetes calls) and safe to run in CI on every config change.
The lint command produces one of three outcomes.
All clear:
Summary: 0 error, 0 warn, 0 info
Warnings to review:
⚠ [TAREL010] gateway.staticIpName — gateway will receive an ephemeral
GCP forwarding-rule address; customer DNS A records may
break on reschedule.
Remediate: set gateway.staticIpName in gcp-gateway.json
or pass --static-ip-name on install.
Summary: 0 error, 1 warn, 0 info
Warnings are advisory; the install proceeds, but each finding should be reviewed. Findings include a remediation hint and a docs link (https://docs.tetrate.ai/tare/lint/<RULE_ID>).
Errors that block install:
✗ [TAREL001] customer — required value cannot be resolved
tried: --customer (empty), gcp-gateway.json:customer (empty),
identity.json:customerId (empty)
Remediate: add "customer" to gcp-gateway.json, or pass --customer.
Summary: 1 error, 0 warn, 0 info
Errors must be fixed before the install runs.
For the full rule list and how to suppress accepted findings (for example via a lint.ignore block in the config), see tare gateway config lint.
CI usage: tare gateway config lint --config gcp-gateway.json --fail-on-warn exits non-zero on any warning or error, making it the recommended pre-merge gate for a config repository.
Step 3: preview the install (plan)
Render a Plan before any side effects:
tare gateway install identity.json \
--type gcp \
--config gcp-gateway.json \
--plan-only
Sample output:
Plan: tare gateway install (provider=gcp)
Identity: identity.json (client_email: [email protected])
Project: withfraser (from --config)
Customer: acme (from --config)
Environment: production (from --config)
Serve host: proxy.acme.tetrate.ai (from --config:serveDomain)
serveUrl: proxy.acme.tetrate.ai (same as serveDomain)
GCP delta:
+ static IP acme-gateway-ip [will create]
+ cert map acme-tetrate-ai [will create]
+ dns auth proxy-acme-…-dns-auth [will create]
+ certificate acme-serve-cert [will create, single host]
+ map entry serve-entry [will create]
Kubernetes delta:
+ namespace tars-gateway [will create]
~ namespace tars-system [exists]
~ namespace tars-dataplane [exists]
+ helm release tars-gateway [will install, chart v1.2.5]
After install, customer must:
Create A record: proxy.acme.tetrate.ai → <address shown after install>
Create CNAME: (provided after install)
Verify the following before proceeding:
- Project is correct. Misclicking between
acme-prodandacme-stagingis the most common deploy mistake. - Customer matches the identity file's owner.
- Serve host matches the customer-facing hostname.
+rows are resources that will be created.~rows already exist and are left alone (or modified in place).- No error findings appear in the inline lint section.
The Plan output is also available as JSON (--output json) for use by other tooling. The JSON shape is a stable contract (apiVersion: tare.tetrate.io/v1alpha1).
Step 4: apply
Two routes are supported.
Option a: CLI provisions GCP resources
When tare is permitted to run gcloud directly:
tare gateway install identity.json \
--type gcp \
--config gcp-gateway.json \
--apply-prereqs \
--wait
--apply-prereqs creates the static IP, DNS authorization, certificate map, certificate, and map entry via gcloud. --wait blocks the command until the gateway has a routable address.
Option b: GCP resources already provisioned
When the platform team owns the GCP resources via Terraform or another IaC tool, skip the prereq automation:
tare gateway install identity.json \
--type gcp \
--config gcp-gateway.json \
--ack-prereqs \
--wait
--ack-prereqs acknowledges that the static IP, certificate map, certificate, and DNS authorization are already in place. The CLI refuses to start the install without one of --apply-prereqs or --ack-prereqs.
Install sequence
The CLI prints the Plan, prompts Proceed? [y/N]: in a TTY (pass --yes in CI), then runs through the following stages:
- Preflight: verify Helm, kubectl, and cluster reachability.
- Cloud prereqs: runs only with
--apply-prereqs. - Namespace creation: idempotent.
- Helm install: applies the gateway chart.
- Wait for address: runs only with
--wait; reports the gateway address once Kubernetes assigns one (typically under a minute).
The final output looks like:
Gateway installation complete.
Customer action required (DNS):
Create DNS A record:
Host: proxy.acme.tetrate.ai
Type: A
Value: 34.110.x.x
TTL: 300
Create DNS authorization CNAME record:
Host: _abc123.proxy.acme.tetrate.ai.
Type: CNAME
Value: abc123.<google-managed>.googleusercontent.com.
Certificate provisioning note:
Certificate may still be PROVISIONING after gateway install.
HTTPS will be fully ready once certificate state is ACTIVE.
Copy these values for use in the next step.
Step 5: configure DNS
Two DNS records are required in the DNS provider. Both must exist; HTTPS does not work without either.
Record 1: a record for customer traffic
| Host | proxy.acme.tetrate.ai (the serveDomain value) |
| Type | A |
| Value | The address printed in Step 4 (for example 34.110.x.x) |
| TTL | 300 (5 minutes) recommended |
Customer clients resolve this record to reach the gateway.
Record 2: DNS authorization cname
| Host | The CNAME host printed in Step 4 (for example _abc123.proxy.acme.tetrate.ai.) |
| Type | CNAME |
| Value | The googleusercontent.com target printed in Step 4 |
| TTL | 300 recommended |
Google Certificate Manager uses this record to verify domain ownership before issuing a TLS certificate. Without it, the certificate stays in PROVISIONING indefinitely.
To fetch the CNAME details at any time after the install:
gcloud certificate-manager dns-authorizations describe \
proxy-acme-tetrate-ai-dns-auth \
--project withfraser \
--format='value(dnsResourceRecord.name,dnsResourceRecord.type,dnsResourceRecord.data)'
Step 6: wait for the certificate to activate
Once both DNS records are live, the managed certificate moves from PROVISIONING to ACTIVE automatically. Typical timing:
- First 5 minutes: DNS propagation; certificate stays
PROVISIONING. - 5–20 minutes: Google validates the DNS authorization, issues the certificate, and the certificate flips to
ACTIVE. - Up to a few hours (rare): when DNS propagation is slow at the registrar.
Check the certificate state:
gcloud certificate-manager certificates describe acme-serve-cert \
--project withfraser \
--format="yaml(name,managed.state,managed.domainStatus)"
Expected output:
name: projects/withfraser/locations/global/certificates/acme-serve-cert
managed:
state: ACTIVE
domainStatus:
proxy.acme.tetrate.ai: ACTIVE
If state: FAILED_NOT_VISIBLE appears, the DNS authorization CNAME from Step 5 is missing or incorrect. Verify that it resolves:
dig +short CNAME _abc123.proxy.acme.tetrate.ai
The result must match the value returned by dns-authorizations describe.
Step 7: verify
Once the certificate is ACTIVE, verify end-to-end.
Cluster resources are healthy:
kubectl get gateway -n tars-gateway
kubectl get httproute -n tars-system
kubectl get httproute -n tars-dataplane
All gateways and routes should report True for the Accepted and Programmed conditions.
Hostname resolution:
dig +short A proxy.acme.tetrate.ai
# Expected: 34.110.x.x (the address from Step 4)
TLS termination and gateway response:
curl -v https://proxy.acme.tetrate.ai/healthz
# Expected: HTTP/2 200, valid certificate for proxy.acme.tetrate.ai
When the healthz endpoint is not exposed, any request that reaches the gateway and returns an Agent Router-shaped response (even 404 or 401) confirms that the gateway and TLS are working.
Troubleshooting
| Symptom | Likely cause | Resolution |
|---|---|---|
customer not resolved, tried: --customer (empty), gcp-gateway.json:customer (empty), identity.json:customerId (empty) | No customer set anywhere. | Add "customer": "<name>" to gcp-gateway.json or pass --customer. |
provider "aws" is not implemented yet | --type aws passed. | Use --type gcp. AWS support is on the roadmap. |
Plan shows a ? row for a resource | The CLI could not probe live state (credentials, IAM, or network issue). | The install treats the resource as will-create. Check the probeError field in --output json for details. |
helm deploy failed | Cluster not reachable, RBAC issue, or a chart-level error. | Run kubectl cluster-info; when that fails, fix the kubeconfig context first. |
Gateway never reports an address (--wait times out) | Cloud load-balancer provisioning is slow, or the static IP is not attached. | Run kubectl describe gateway -n tars-gateway and check Kubernetes events for the gateway resource. |
Certificate stuck PROVISIONING for >30 min | DNS authorization CNAME is missing, wrong, or not yet propagated. | Re-check Step 5. Use dig to verify the CNAME resolves. |
Certificate state FAILED_NOT_VISIBLE | DNS authorization record cannot be resolved from Google's side. | Recreate the CNAME exactly as printed by dns-authorizations describe. |
curl returns a TLS error after cert is ACTIVE | A record points to the wrong IP, or local DNS cache. | Verify with dig; flush the local resolver. |
Re-running install fails with already exists from gcloud | gcloud.skipExisting is false. | Set "gcloud": { "skipExisting": true } in gcp-gateway.json. |
For issues not covered above, re-run the install with the Plan visible (--plan-only is sufficient) and share the output with the Tetrate contact. The Plan captures every resolved value and live state probe, which is usually enough to diagnose.
Need help?
When blocked, capture:
- The output of
tare gateway install … --plan-only. - The output of
tare gateway config lint --format json. - The output of
gcloud certificate-manager certificates describe ….
Share these with the Tetrate contact. Together they describe the config, the live cloud state, and the certificate state, which is usually enough to unblock without further back-and-forth.
Where to go next