For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Portal auth server
Deploy a dedicated portal auth server to isolate data-plane credential validation from admin traffic.
Note
This guide must be completed by a Portal admin.
About the portal auth server
When you deploy a portal, a single portal web server is deployed that handles two distinct workloads:
- Portal admin traffic: Low-frequency CRUD operations for managing teams, apps, subscriptions, and credentials by using the portal frontend.
- Portal data plane traffic: The
GET /v1/metadataendpoint, which ExtAuth calls on every inbound API request to validate the caller’s API key or OAuth token. These are the credentials that portal users create through the portal frontend to access API products. This endpoint is called at high frequency and must respond with low latency.
Because both workloads share the same process, database connection pool, and fault domain, a burst of admin activity can consume resources that slow or interrupt the GET /v1/metadata endpoint and can lead to API request failures.
To prevent resource starvation or high latency on the GET /v1/metadata endpoint, you can deploy a separate portal auth server by adding the spec.authServer block to your PortalParameters resource. The auth server runs in restricted mode with the following settings:
- Serves only
/healthz,/readyz, andGET /v1/metadataendpoints. All other routes return404. - Skips database schema migrations, API product informers, and the admin API.
- Can be configured with independent replica counts, resource limits, and PostgreSQL credentials.
Note
The portal auth server handles only API key and OAuth token validation. The OIDC login flow for portal user authentication (/v1/login and /v1/logout paths) continues to run on the main portal web server.
Consider deploying a separate portal auth server when:
- You need credential validation to remain available and responsive, even during load spikes in admin operations or portal web server restarts.
- You want to scale the portal data plane component independently of the portal admin component.
- You want credential validation queries to hit a read-only PostgreSQL replica instead of your primary database to reduce read operations.
- Your environment has strict fault isolation requirements between control plane and data plane components.
Before you begin
- Complete the Get started with portal guide to install the portal controller and create a portal.
- Configure PostgreSQL as your portal database by following the Portal database guide. The portal auth server requires a persistent relational database. You cannot use the default in-memory storage that is built into the portal.
- Set up API key management by following the API keys guide. The verification step in this guide requires an
AuthConfigwith aportalAuth.urlalready in place to confirm that credential validation routes through the auth server. - Create an API key in the portal frontend. You use this key to make a test request in the verification step.
Step 1: Deploy the portal auth server
Add an authServer block to your PortalParameters resource. The presence of this block deploys the Portal Auth Server.
Update the
PortalParametersresource to add theauthServerblock.kubectl apply -f- <<EOF apiVersion: portal.solo.io/v1alpha1 kind: PortalParameters metadata: name: portal-params namespace: default spec: store: postgres: secretRef: name: portal-postgres-secret authServer: replicas: 1 logLevel: debug EOFNote
When the
authServer.postgresfield is not set, the portal auth server inherits the PostgreSQL connection details from thespec.store.postgresfield.Field Description authServer.replicasNumber of portal auth server replicas. authServer.logLevelLog level for the portal auth server. Supported values include debug,info,warn, orerror.authServer.resourcesCPU and memory resource requests and limits for the portal auth server container. authServer.postgres.secretRef.nameName of a Kubernetes secret with PostgreSQL connection details. Set this field to point the auth server at a separate PostgreSQL instance or read replica. If omitted, the auth server uses the same connection details as the portal admin web server. For example setup steps, see the Optional: Use a separate Postgres instance guide. Verify that the portal auth server deployment and service are created. The auth server is named
portal-auth-<portal-name>.kubectl get deployment -n default | grep portal-auth kubectl get service -n default | grep portal-authExample output:
portal-auth-my-portal 2/2 2 2 3m3s portal-auth-my-portal ClusterIP 10.96.53.219 <none> 8080/TCP 3m3s
Step 2: Update your AuthConfigs
Update the portalAuth.url field in the AuthConfig resources that you use to protect your API products to point to the portal auth server service (portal-auth-my-portal) instead of the combined portal web server (portal-my-portal). Select the tab that matches your credential type.
kubectl apply -f- <<EOF
apiVersion: extauth.solo.io/v1
kind: AuthConfig
metadata:
name: httpbin-auth
namespace: default
spec:
configs:
- name: httpbinAuth
portalAuth:
url: http://portal-auth-my-portal.default.svc.cluster.local:8080
cacheDuration: 10s
apiKeyHeader: "api-key"
EOFkubectl apply -f- <<EOF
apiVersion: extauth.solo.io/v1
kind: AuthConfig
metadata:
name: httpbin-auth
namespace: default
spec:
configs:
- name: oauth2Validation
oauth2:
accessTokenValidation:
jwt:
remoteJwks:
url: "$KEYCLOAK_URL/realms/portal/protocol/openid-connect/certs"
- name: httpbinAuth
portalAuth:
url: http://portal-auth-my-portal.default.svc.cluster.local:8080
EOFStep 3: Verify the portal auth server
Confirm that credential validation requests route through the portal auth server and not the portal web server. Select the tab for your credential type.
Set your API key as an environment variable. Use the API key you created in the Before you begin section.
export API_KEY="<your-api-key>"Send a request to your API product.
curl -vik localhost:8080/httpbin/headers \ -H "host: api.example.com" \ -H "api-key: $API_KEY"Check the portal auth server logs for the
/v1/metadatacall. Note that it might take a few minutes for the logs to show.kubectl logs deployment/portal-auth-my-portal -n default --tail=50 | grep "v1/metadata"Example output:
{"time":"2026-07-16T19:15:26.007809131Z","level":"debug","msg":"http request","component":"portal/webserver/access", "role":"portal-auth","method":"GET","path":"/v1/metadata","query":"apiKey=%3Credacted%3E&apiProductId=httpbin","status":200, "duration_ms":0}Confirm that the portal web server did not receive the
/v1/metadatacall. An empty response confirms that these requests are not routed through the portal web server.kubectl logs deployment/portal-my-portal -n default --tail=50 | grep "v1/metadata"
Set the OAuth client credentials that you received from the portal as environment variables.
export OAUTH_CLIENT="<your-oauth-client-id>" export OAUTH_SECRET="<your-oauth-client-secret>"Request an access token from your IdP using the client credentials.
export ACCESS_TOKEN=$(curl -X POST -Ssm 10 --fail-with-body \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "client_id=${OAUTH_CLIENT}" \ -d "client_secret=${OAUTH_SECRET}" \ -d "grant_type=client_credentials" \ "$KEYCLOAK_URL/realms/portal/protocol/openid-connect/token" | jq -r .access_token) echo $ACCESS_TOKENSend a request to your API product by using the access token.
curl -vik localhost:8080/httpbin/headers \ -H "host: api.example.com" \ -H "Authorization: Bearer $ACCESS_TOKEN"Check the portal auth server logs for the
/v1/metadatacall.kubectl logs deployment/portal-auth-my-portal -n default --tail=50 | grep "v1/metadata"Example output:
{"time":"2026-07-16T19:15:26.007809131Z","level":"debug","msg":"http request","component":"portal/webserver/access", "role":"portal-auth","method":"GET","path":"/v1/metadata","query":"accessToken=%3Credacted%3E&apiProductId=httpbin","status":200, "duration_ms":0}Confirm that the portal web server did not receive the
/v1/metadatacall. An empty response confirms that these requests are not routed through the portal web server.kubectl logs deployment/portal-my-portal -n default --tail=50 | grep "v1/metadata"
Optional: Use a separate Postgres instance
For stronger fault isolation, you can point the portal auth server at a separate PostgreSQL instance so that admin database activity does not affect credential validation latency.
Create a secret with the connection details for the separate Postgres instance.
kubectl apply -f- <<EOF apiVersion: v1 kind: Secret metadata: name: portal-auth-postgres-secret namespace: default type: Opaque stringData: host: "<read-replica-host>" port: "5432" username: "<username>" password: "<password>" database: "<database>" sslmode: "disable" EOFReference the secret in the
authServer.postgresblock.kubectl apply -f- <<EOF apiVersion: portal.solo.io/v1alpha1 kind: PortalParameters metadata: name: portal-params namespace: default spec: store: postgres: secretRef: name: portal-postgres-secret authServer: replicas: 2 postgres: secretRef: name: portal-auth-postgres-secret EOF
Note
The portal auth server does not run database schema migrations. Ensure that the separate PostgreSQL instance stays in sync with the primary database schema.