Install from the P0 console
Deploy the gateway with the Terraform the P0 console generates, and register it with P0 in the same pass.
The console installer collects your environment's details, generates the Terraform that deploys the gateway, and registers the gateway with P0 in the same pass. Before you start, check the prerequisites.
1. Start the installer
Navigate to Integrations on p0.app and select Agentic gateway. The page lists four components, each labelled preview:
Gateway
The runtime itself, deployed into your cluster.
Yes. This page installs it.
MCP server
Declares an MCP server the gateway fronts.
No. Configure one after the gateway is running.
A2A bridge
Puts an upstream agent host behind the gateway, so agent-to-agent calls pass through it.
No.
Points P0 at the logs holding the gateway's tool-call activity.
No. The gateway still ships access logs without one.
The other three configure what runs behind the gateway, and each needs one to attach to, so start here. The preview label means a component may have limited features or support. Contact P0 with questions about a specific component before you build on it.
Choose the Gateway component and click Add gateway. Fill in the form, then click Next:

Gateway identifier: a name for this gateway within P0. Use lowercase letters, digits, and hyphens, starting and ending with a letter or digit, up to 53 characters. It also becomes the Helm release name and the Gateway class, and, for a P0-hosted domain, part of the hostname.
Domain hosting: how your gateway's public hostname and DNS records are managed.
P0-hosted domain: P0 assigns a hostname derived from your organization and gateway identifier, and creates its DNS record for you in step 4. Nothing to register or manage yourself.
Self-hosted domain: you bring your own domain. Enter the Agentic gateway URL where your gateway will run, and, if your OAuth/JWKS endpoint runs on a different host than the gateway itself, the OAuth server endpoint. It defaults to the gateway URL. You create the DNS record yourself in step 4.
Let's Encrypt contact email: the address for the ACME account that issues your gateway's TLS certificate. Used for renewal notices and account recovery. Not published.
Upstream OIDC client ID: the OAuth 2.0 client ID from the identity provider your end users authenticate against. The client must allow the
openid,email, andprofilescopes. On a provider with a consent screen such as Google, the screen has to be configured before anyone signs in. An existing client works as well as a new one if its authorized redirect URIs includehttps://<gateway-host>, the gateway's own URL. Adding that URI to a client's existing list doesn't disturb its other entries. If you reuse a client that other applications also use, removing the URI is part of tearing this gateway down. See redirect URI mismatch if sign-in fails after you enter this client.Gateway storage: where the bundled PostgreSQL and VictoriaLogs keep their data.
Kubernetes storage class: enter the
StorageClassyour cluster provides in Storage class name. On an EKS Auto Mode cluster, this is the class you create in EKS Auto Mode storage.No persistent storage (proof of concept only): for a cluster that has no storage class. The gateway loses its data whenever a pod is rescheduled, so choose this only to evaluate the gateway. See Evaluating without a storage class.
Kubernetes namespace (optional): defaults to
p0-agentic-gateway.
Clicking Next assigns this gateway a P0 service account. The Terraform on the next page already authorizes that account to call your gateway's management API, so there is nothing to reconcile by hand.
2. Deploy with the generated Terraform
The console now shows a Terraform snippet, pre-filled with everything from step 1 plus your tenant's P0 URL and audience and the assigned service account:

Copy it into your Terraform configuration and apply it:
terraform init downloads the providers the configuration names. terraform apply shows you what it proposes to create and waits for you to type yes, then installs the gateway into your cluster. The apply takes a few minutes, most of it waiting for your cloud provider to give the gateway a load balancer address. It finishes with a line reading Apply complete! and a count of the resources it added.
Leave the console open. You return to it in step 4.
The snippet pins a module version for you. The module is published on the Terraform Registry as p0-security/p0-ai-gateway-stack/kubernetes, where you can see the other published versions and what each one installs.
Only the values needed to get started are filled in. To customize further for observability, backend container settings, or a non-default OAuth client, add values to the module's values block before applying. See the Helm chart values reference.
The generated Terraform requests production Let's Encrypt certificates. Those are rate-limited per hostname, so if you expect to tear this deployment down and rebuild it repeatedly, set letsEncrypt.env to staging while you iterate, and switch it back to prod before you test the backends.
3. Set your OIDC client secret
OIDC_CLIENT_SECRET starts as a placeholder, since only you have the real value. Sign-in fails until you replace it, with the gateway itself showing "Authentication failed. Please try again." if you skip this step or forget to restart.
Run this on its own first. It prompts for your client secret and reads it without showing it, so the value stays off your screen and out of your shell history:
Then write it into the Secret and restart the Auth Server:
The kubectl patch command writes your client secret into app-secrets, the Kubernetes Secret the gateway reads its credentials from, replacing the placeholder value and leaving the other keys alone. It reads the patch from standard input, so the secret never appears in the command's arguments, where other users of the machine could see it. unset clears the value from your shell. The last command replaces the running Auth Server pod with a new one, which picks the value up.
You must restart: agentic-auth-server is the only workload that reads OIDC_CLIENT_SECRET, and it loads app-secrets into its environment at pod startup, so a running pod keeps the placeholder until it's replaced.
If you're using your own database instead of the bundled PostgreSQL, patch POSTGRESQL_PASSWORD the same way, then restart both Deployments, since both connect to the database:
The job's log says whether the placeholder still needs replacing, along with everything else it did:
4. Register your gateway deployment
Report the LoadBalancer address
Back in the console, the LoadBalancer address field asks for the address your cluster assigned the gateway. Read it back with the command the console shows:

This reads back the address your cloud provider assigned the gateway's load balancer. An empty result means the address has not been assigned yet. Wait a minute and run it again.
Paste the result into the field. What happens next depends on the domain hosting you chose in step 1:
P0-hosted domain: P0 creates the DNS record for your assigned hostname pointing at this address. You don't need to do anything else.
Self-hosted domain: create the record with your own DNS provider. Use an
Arecord for an IPv4 address,AAAAfor IPv6, orCNAMEif your cloud provider gave you a hostname instead of a static IP. If your gateway's hostname is a bare domain (a "zone apex"), your DNS provider likely can't accept aCNAMEthere. Use itsALIAS/ANAMEequivalent, or a CNAME-flattening feature, instead.
Note your gateway's hostname before you move on. When formatted as https://<hostname>, it is the gateway's OIDC issuer, and is used as the OIDC provider URL if you install AWS OIDC or GCP Workload Identity Federation to give agents cloud access.
Provide the management tunnel public key
Every gateway deployment also runs a tunnel that dials out to P0 so P0 can reach the gateway's management API without any inbound port ever being opened. Registering its public key with P0 is required for every new gateway β it's what lets P0 manage a gateway with no public exposure: no resolvable DNS record, no issued TLS certificate.
Read the key from the running gateway:
Paste its output into the Management tunnel public key field. Once registered, P0 routes every management call for this gateway (sync, entitlements, activity log reads, and the reachability check below) through the tunnel instead of the gateway's direct URL.
Click Finish.
Clicking Finish also has P0 confirm it can reach the gateway over the tunnel.
5. Verify
Confirm the pods are running:
Every row should read Running, except the install's own jobs, which read Completed.
The management tunnel is only how P0 reaches the gateway. MCP clients may still connect to the gateway over public DNS, and some integrations β AWS OIDC, for example β need the gateway's JWKS endpoint reachable directly, so DNS and the certificate matter regardless of whether you registered a tunnel key.
Confirm DNS resolves and the certificate is Ready (this may take several minutes):
This asks whether Let's Encrypt has issued the gateway's TLS certificate, which it can only do once your DNS record resolves.
Desired output:
Confirm both backends respond through the gateway host over public DNS. Each curl command below makes one request and prints only the HTTP status code it got back. In order, expect:
200from the Auth Server, on its exact-match route200from the gateway, on its catch-all route301from plain HTTP, which is redirected to HTTPS
Replace <gateway-host> with your gateway's hostname:
The gateway appears in the console with the state Installed once the check passes.
Next steps
Once the gateway is deployed, configure the MCP servers it fronts:
AI gateway integration: configure upstream MCP servers, enroll identity providers, and add A2A bridges.
Last updated