Fairway services can now expose more than one port
Until now a Fairway manifest could declare exactly one service-facing port, through spec.containerPort and an optional spec.protocol. A service that publishes gRPC to other workloads in the cluster alongside an HTTP endpoint for the outside world simply could not be described. Several teams have asked for this, and
Lab Bench had already landed a listeners shape of its own, so the two were drifting apart.
Fairway v3.5.0 replaces both fields with spec.listeners, a list with one entry per port:
spec:
listeners:
- port: 8080
protocol: http
routing: EXTERNAL
- port: 9000
protocol: grpc
routing: INTERNALEach listener carries three fields. port is the TCP port the container binds. protocol is http or grpc, and selects both the port name on the generated Service and the form of the health-check probes (httpGet for HTTP, the native Kubernetes gRPC probe for gRPC). routing is INTERNAL or EXTERNAL, and it decides which port the generated Ingress or HTTPRoute targets.
routing defaults to INTERNAL, so routing a port outside the cluster is always something you ask for explicitly. It selects how traffic reaches a port, not who is allowed to send it: an INTERNAL listener is still reachable by anything already running in the cluster, so do not read it as an access-control boundary.
What you get in the generated chart
One ClusterIP Service exposes every listener as a named port, with a matching containerPort entry on the Deployment for each. A grpc listener additionally gets a headless Service, for gRPC client-side load balancing. Ingress and HTTPRoute resources, when an operator enables them, are generated only for the EXTERNAL listener.
Probes and the PORT environment variable key off a single listener: the EXTERNAL one when there is one, otherwise the first declared. Both are about the container rather than about external routing, so a cluster-local service still gets probes and still gets PORT.
Current limits
Fairway validates the list and rejects a manifest that breaks any of these:
- At most two listeners per service.
- One listener per protocol, so no two
httplisteners on the same service. - At most one
EXTERNALlistener. - Unique ports across listeners.
- A service with two listeners must mark one of them
EXTERNAL.
The cap of two is deliberate and matches the consensus on
fairway#31: one HTTP and one gRPC covers every request received so far, and raising it later is cheaper than walking back a shape nobody needed. A per-listener name field was considered and deferred for the same reason.
What to do
Migrate your manifest. spec.containerPort and spec.protocol are deprecated but still accepted: Fairway rewrites them into an equivalent single EXTERNAL listener when it loads your manifest, so an existing manifest keeps generating an identical chart. You will see a deprecation warning when you run fairway generate or fairway validate.
Port the two fields over by hand. The one thing you cannot do is set both: a manifest carrying listeners alongside containerPort or protocol is rejected rather than silently resolved, because there is no correct way to guess which the author meant.
The service-owner guide documents the field in full, and the Fairway component page covers how chart generation fits together.