Tutorials
These examples are intentionally small. Use them as starting points, then apply the provider-specific guidance from GKE or AWS before production rollout.
Case 1: Expose One HTTP Service
Section titled “Case 1: Expose One HTTP Service”Use this when one backend should be reachable on a known public port.
apiVersion: v1kind: Servicemetadata: name: web namespace: appspec: type: LoadBalancer loadBalancerClass: svc-mux.nowake.ai/mux.svc-mux allocateLoadBalancerNodePorts: false selector: app: web ports: - name: http protocol: TCP port: 80 targetPort: httpExpected mapping:
http:80->80Case 2: Keep A Backend Port But Use A Different Public Port
Section titled “Case 2: Keep A Backend Port But Use A Different Public Port”Use this when pods listen on one port but clients must connect to another public port.
apiVersion: v1kind: Servicemetadata: name: web-alt namespace: appspec: type: LoadBalancer loadBalancerClass: svc-mux.nowake.ai/mux.svc-mux allocateLoadBalancerNodePorts: false selector: app: web-alt ports: - name: http protocol: TCP port: 30080 targetPort: httpNo external-ports annotation is needed because port: 30080 is already the
desired mux public port.
Use the annotation only if the channel Service port must remain different:
metadata: annotations: svc-mux.nowake.ai/external-ports: "http:30080"spec: ports: - name: http port: 8080 targetPort: httpExpected mapping:
http:8080->30080Case 3: Allocate Public Ports Automatically
Section titled “Case 3: Allocate Public Ports Automatically”Use this when users do not care which public port each channel receives, but they need stable assignments after allocation.
Configure the mux range:
defaultLoadBalancer: portRange: "20000-20099" maxPorts: 100Request automatic allocation:
apiVersion: v1kind: Servicemetadata: name: worker-api namespace: app annotations: svc-mux.nowake.ai/external-ports: "http:auto"spec: type: LoadBalancer loadBalancerClass: svc-mux.nowake.ai/mux.svc-mux allocateLoadBalancerNodePorts: false selector: app: worker-api ports: - name: http port: 8080 targetPort: httpCheck the assigned port:
kubectl get svc worker-api -n app \ -o go-template='{{index .metadata.annotations "svc-mux.nowake.ai/ports"}}{{"\n"}}'Case 4: Migrate Existing LoadBalancer Services
Section titled “Case 4: Migrate Existing LoadBalancer Services”Use this when a workload already has a normal provider-backed LoadBalancer
Service and you want to test the mux path before cutting DNS.
- Keep the existing Service in place.
- Create a second channel Service with a distinct name and the same selector.
- Set
spec.loadBalancerClassto the mux class. - Set
spec.ports[].portto the public port you want on the mux. - Set
targetPortto the same backend port used by the old Service. - Verify the mux IP or hostname and TCP connectivity.
- Move DNS only after the mux path is validated.
Example channel:
apiVersion: v1kind: Servicemetadata: name: app-mux namespace: appspec: type: LoadBalancer loadBalancerClass: svc-mux.nowake.ai/mux.svc-mux allocateLoadBalancerNodePorts: false selector: app: app ports: - name: public protocol: TCP port: 20301 targetPort: app-publicThe channel port 20301 is meaningful: it is the mux public port. The
targetPort points at the backend pod port.
Case 5: Expose P2P Or Other Raw TCP Workloads
Section titled “Case 5: Expose P2P Or Other Raw TCP Workloads”Use one channel per backend component when different components expose different target ports. Do not combine unrelated components under one selector just to share a Service.
apiVersion: v1kind: Servicemetadata: name: node-p2p namespace: chainspec: type: LoadBalancer loadBalancerClass: svc-mux.nowake.ai/mux.svc-mux allocateLoadBalancerNodePorts: false selector: app: chain-node component: node ports: - name: p2ptcp protocol: TCP port: 20301 targetPort: p2ptcp---apiVersion: v1kind: Servicemetadata: name: execution-p2p namespace: chainspec: type: LoadBalancer loadBalancerClass: svc-mux.nowake.ai/mux.svc-mux allocateLoadBalancerNodePorts: false selector: app: chain-node component: execution ports: - name: p2p protocol: TCP port: 10301 targetPort: p2pExpected mappings:
p2ptcp:20301->20301p2p:10301->10301Case 6: Split Capacity Across Multiple Muxes
Section titled “Case 6: Split Capacity Across Multiple Muxes”Use multiple muxes when provider limits, blast radius, ownership, or port ranges should be separated.
Example channel references:
spec: loadBalancerClass: svc-mux.nowake.ai/payments.platformspec: loadBalancerClass: svc-mux.nowake.ai/rollup-p2p.platformKeep automatic allocation ranges separate:
payments: 20000-20099rollup-p2p: 20100-20199On GKE, one mux is limited to 100 Service ports. Split high-port workloads before reaching that limit.
Case 7: Manage Muxes With GitOps
Section titled “Case 7: Manage Muxes With GitOps”Use GitOps for desired provider settings and channel specs, but ignore controller-owned runtime fields.
Git should own:
- mux Service name, namespace, labels, provider annotations,
type, static IP, and load balancer class. - channel Service selectors,
loadBalancerClass, ports, and user annotations.
Git should not own:
- mux
spec.ports. - generated mux Endpoints.
- controller-owned annotations such as
svc-mux.nowake.ai/ports,svc-mux.nowake.ai/channels,svc-mux.nowake.ai/topology, andsvc-mux.nowake.ai/summary. - mux state ConfigMap contents, including automatic and static port claims.
Use GitOps compatibility for Argo CD and Flux examples.