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
Enable ingress for the three public surfaces
The chart renders one Ingress each for the frontend, the backend, and Kratos.
Each is created only when
ingress.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.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.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:An empty
allowedOrigins skips the rule entirely rather than defaulting to
something permissive.Route MCP server instances
When Instances are then reachable at
mcpManager.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.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.The
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 to localhost:4433
The site loads but login redirects to localhost: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
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
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
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
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
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
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 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.
Related
Deploy on Kubernetes with Helm
Install the agentarea Helm chart, decide which bundled dependencies to keep
Configuration
Every environment variable each AgentArea service reads, and the Helm value
that sets it
Requirements
Host, cluster, and dependency versions required to run AgentArea, per
deployment target
Troubleshoot a self-hosted deployment
Diagnose a deployment that will not start