IPv6 and dual-stack
This guide covers running the Helm-installed Arcade platform on a dual-stack Kubernetes cluster, and lists the parts that still assume IPv4.
Dual-stack is the supported way to serve Arcade over IPv6. IPv6-only clusters aren’t supported: several parts of the chart and the still use IPv4-only addresses (see What doesn’t support IPv6).
When you need it
- Your cluster is dual-stack and clients reach Arcade over IPv6.
- Your network policy requires Services to expose both an IPv4 and an IPv6 ClusterIP.
Values overlay
global:
bindAddress: "::"
ipFamilyPolicy: PreferDualStack
ipFamilies: [IPv4, IPv6]You usually need both halves:
global.bindAddresssets the address the HTTP servers of the , coordinator, Experience API, and workers listen on. The dashboard and OpenFGA don’t read it; the dashboard already listens on both IPv4 and IPv6. The default0.0.0.0listens on IPv4 only.::listens on IPv6 and, on Linux, also accepts IPv4 through IPv4-mapped addresses. Brackets are stripped, so[::]works too.global.ipFamilyPolicyandglobal.ipFamiliessetspec.ipFamilyPolicyandspec.ipFamilieson the , coordinator, dashboard, Experience API, OpenFGA, and worker Services. The bundled PostgreSQL and Redis Services ignore them and stay on the cluster’s default family; the bundled PostgreSQL Service is headless, so it has no ClusterIP at all.ipFamilyPolicytakesSingleStack,PreferDualStack, orRequireDualStack. The first entry inipFamiliespicks the Service’s primary ClusterIP. If you leave both unset, Services get your cluster’s default, usually IPv4 single-stack.
PreferDualStack falls back to a single family on a cluster that isn’t dual-stack. Use RequireDualStack if you’d rather the install fail there.
If you enable networkPolicy.enabled, networkPolicy.egressCIDRs takes IPv6 ranges as well as IPv4 ranges; add the IPv6 ranges Arcade pods need to reach.
Verify it
# Each Arcade Service lists both families and two ClusterIPs (bundled PostgreSQL and Redis stay single-stack)
kubectl -n arcade get svc -o custom-columns=NAME:.metadata.name,FAMILIES:.spec.ipFamilies,IPS:.spec.clusterIPs
# Pods have an IPv6 address
kubectl -n arcade get pods -o wideThen reach the ’s IPv6 ClusterIP from a pod in the cluster, for example curl -g "http://[<ipv6-cluster-ip>]:9099/v1/health".
Common failures
| Symptom | Likely cause and fix |
|---|---|
| Services have an IPv6 ClusterIP, but IPv6 connections are refused | global.bindAddress is still 0.0.0.0. Set it to ::. |
| Services stay single-stack | global.ipFamilyPolicy is unset or SingleStack, or the cluster isn’t dual-stack. Set PreferDualStack, and check the cluster’s Service CIDRs. |
RequireDualStack Services fail to create | The cluster has no IPv6 Service CIDR. Enable dual-stack on the cluster, or use PreferDualStack. |
| Deployed MCP servers can’t reach an IPv6-only destination | Expected: their default egress allows public IPv4 only. See below. |
What doesn’t support IPv6
These parts are IPv4-only today. Plan around them, especially for Arcade Deploy:
- Deployed servers’ outbound traffic. The NetworkPolicy the applies to each deployed server allows public egress only to
0.0.0.0/0, minus private and cluster ranges. There’s no IPv6 equivalent, so deployed servers get no IPv6 egress by default. Eachdeployments.egressAllowlistentry becomes its own NetworkPolicy rule, so an IPv6 range listed there may open egress to it, but that path isn’t tested. - The private ranges deployed servers are blocked from are IPv4 (
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10,169.254.0.0/16). Don’t put IPv6 ranges indeployments.network.clusterCIDRs: they’re added as exceptions to the IPv40.0.0.0/0rule. - External DNS for deployed servers. With
deployments.network.dns.mode: external,deployments.network.dns.nameserversmust be public IPv4 addresses. The refuses to start with an IPv6 nameserver. - Deployed servers’ Services don’t set
ipFamilyPolicyoripFamilies, so they get the cluster’s default family.global.ipFamilyPolicydoesn’t apply to them.
The Arcade Engine’s private-address block (for worker, server, and OAuth URLs) does cover IPv6: it blocks ::1, ::, fe80::/10, fc00::/7, and ff00::/8 alongside the IPv4 private ranges. IPv6 CIDRs and addresses work in engine.ssrfAllowlist.
What you own and what the chart handles
| You | The chart |
|---|---|
| A dual-stack cluster: pod and Service CIDRs for both families, and a CNI that routes IPv6 | Bind address for Arcade pods (global.bindAddress) |
IPv6 load balancing, DNS AAAA records, and your Gateway or ingress listeners | ipFamilyPolicy and ipFamilies on every chart-rendered Service |
| IPv6 reachability of the bundled PostgreSQL and Redis, or external data stores | IPv6 ranges you list in networkPolicy.egressCIDRs |
| Keeping Arcade Deploy egress needs on IPv4 until IPv6 is supported |