Configure networking and ingress
Getting AgentArea onto a hostname takes more than an Ingress resource. Three separate things have to agree: what the Ingress routes, what URL the backend advertises for itself, and what URL the browser is redirected to for authentication. Set only the first and you get a site that loads and cannot log in. This guide also covers egress restriction for the pods that run code the platform did not write — MCP server instances and agent sandboxes — which is a different problem with a different mechanism.Prerequisites
- A Kubernetes install of the chart, see Kubernetes
- An ingress controller matching the
ingress.classNameyou intend to set - DNS records you control for the hostnames below
- For egress restriction: a CNI that enforces NetworkPolicy
Steps
1. Enable ingress for the three public surfaces
The chart renders one Ingress each for the frontend, the backend, and Kratos. Each is created only wheningress.enabled is true and that host is
non-empty — so an empty kratos.host silently produces no Kratos Ingress, and
login breaks with no error at install time.
ingress.annotations and ingress.tls are shared across all three Ingresses.
There is no per-host override.
2. Set the URLs the platform advertises
These are separate from routing. They are the URLs baked into responses, and they must be what a client can reach, not what a pod can reach.global.api.publicUrl has a fallback chain: when empty, the chart derives
https://<ingress.hosts.backend.host> if ingress is enabled, otherwise the
internal ClusterIP URL. The derived value assumes https. If you terminate TLS
elsewhere and serve plain HTTP, set publicUrl explicitly.
kratos.urls.publicBrowser defaults to kratos.urls.public. Set it separately
only when pods cannot resolve the public domain — a split-horizon DNS setup, or
a Tailscale-style overlay where the in-cluster name and the browser-facing name
differ.
global.storage.publicEndpoint is empty by default, and the platform then falls
back to the internal endpoint. That works only when the object store is itself
browser-reachable, which in a cluster it usually is not. Leaving it empty is the
single most common cause of file uploads failing in an otherwise working
install.
3. Allow browser uploads through CORS
Presigned uploads go from the browser straight to the object store, bypassing the API, so the object store needs its own CORS rule. The chart applies one to the artifacts bucket during bootstrap:allowedOrigins skips the rule entirely rather than defaulting to
something permissive.
4. Set the Kratos cookie domain
localhost. A session cookie scoped to localhost is not sent
to app.example.com, so login appears to succeed and every subsequent request
is anonymous.
Kratos also has its own CORS allow-list under kratos.config.serve.public.cors.allowed_origins,
which ships pointing at staging-0.agentarea.ai. Replace it with your origins.
5. Route MCP server instances
WhenmcpManager.backend is kubernetes and gateway_api is in
mcpManager.features.enabled, the manager creates an HTTPRoute per MCP server
instance, attached to a Gateway you provide.
https://<domain>/mcp/<instance-name>. The
chart does not install Gateway API CRDs or a Gateway; both must exist already.
6. Restrict egress from untrusted pods
MCP server instances run user-supplied images. The chart ships a NetworkPolicy, on by default, that denies them every cluster-internal and link-local range while allowing DNS and the public internet.169.254.0.0/16 entry covers the cloud metadata endpoint at
169.254.169.254, which is the concrete thing this policy exists to block,
along with kube-apiserver access and lateral movement to other tenants’ instance
pods. Ingress is deliberately left open: inbound is fronted by the Service and
gateway, and cross-pod reachability is already cut by every instance’s egress
deny.
Add your cluster’s pod and service CIDRs if they fall outside these ranges. Some
providers use 100.64.0.0/10, which the defaults do not cover.
Two limits worth stating plainly:
- This is a no-op on a cluster whose CNI does not enforce NetworkPolicy. The chart cannot detect that, and nothing warns you. The policy object exists, and untrusted pods reach the metadata endpoint anyway.
- This is not kernel isolation. Egress rules constrain the network; they do nothing about a container escape. That is what
mcpManager.runtimeClassis for, and it defaults to"".
app.kubernetes.io/managed-by: mcp-manager and
app.kubernetes.io/component: mcp-server. The component constraint is
deliberate: workflow sandboxes are also manager-created, and locked sandboxes
must not inherit this public-egress allowance. They are governed by
mcpManager.warmPool.lockedNetworkPolicy instead, which grants no public egress
at all — add stable S3 endpoint CIDRs to s3EgressCIDRs if your object store is
outside the cluster. The template rejects broad public routes there.
Verify
Confirm the Ingresses exist. Three, not one:host value is empty
in values.yaml.
Confirm the backend advertises the right URL — this catches the derived-value
fallback silently pointing at a ClusterIP name:
cookieDomain, which no curl will
reveal.
Confirm the egress policy is both present and enforced:
Troubleshooting
The site loads but login redirects tolocalhost:4433. kratos.urls.public
is unset, so the chart used the internal service URL. Set kratos.urls.public
and kratos.urls.publicBrowser.
Login succeeds, then every request is unauthenticated.
kratos.session.cookieDomain is still localhost. Set it to your parent
domain.
Login fails with a CORS error in the browser console. Kratos rejects the
origin. kratos.config.serve.public.cors.allowed_origins ships pointing at a
staging domain; replace it.
File uploads fail with a DNS error on an S3 hostname.
global.storage.publicEndpoint is empty and the presigned URL points at the
in-cluster object store. Set it, and set global.storage.cors.allowedOrigins.
Uploads reach the object store and are rejected with a CORS error. The
endpoint is right but the bucket has no CORS rule. allowedOrigins was empty at
bootstrap, which skips applying one.
MCP instances are created but never become reachable. No Gateway matching
mcpManager.gateway, or Gateway API CRDs are not installed. The manager creates
HTTPRoutes that nothing programs.
Provider icons are broken in the UI. API_BASE_URL resolved to an internal
address. Icons are served by the backend at that URL; set
global.api.publicUrl.
The egress NetworkPolicy exists and untrusted pods still reach the metadata
endpoint. The CNI does not enforce NetworkPolicy. Nothing in the chart detects
this. Switch to a CNI that enforces it, or accept that MCP instance pods have
cluster-internal network access.