Skip to content

Migration: server bind address & loopback metrics default

This batch threads a bind address through the managed server stack, hardens a default that previously exposed a diagnostics surface on all interfaces, and turns a silently-swallowed port error into a hard failure. It ships as part of the transport-stack follow-ups (go/transport v0.2.0, go/transport-metrics v0.4.0).

What changed

1. Bind address is now configurable (additive)

pkg/http and pkg/grpc server settings gain a Host field, surfaced as the config keys server.http.host and server.grpc.host (and any custom prefix, e.g. server.admin.host). Both default to "", which binds all interfaces (0.0.0.0 / [::]) — unchanged from previous releases.

server:
  http:
    port: 8080
    host: 127.0.0.1   # NEW: restrict the HTTP listener to loopback
  grpc:
    port: 9090
    host: 127.0.0.1   # NEW: restrict the gRPC listener to loopback

The transport constructors also accept a programmatic transporthttp.WithHost(...) / transportgrpc.WithHost(...) option (alias WithBindAddress) which overrides the configured value.

Action: none required. Opt in by setting host on any listener that should not be reachable off-host — management, admin, metrics, and pprof endpoints in particular.

2. Standalone metrics server now defaults to loopback

Behaviour change — the metrics/pprof server binds loopback by default

The standalone metrics server shipped by go/transport-metrics (v0.4.0) now defaults its bind host to 127.0.0.1 instead of the previous implicit all-interfaces bind. A WithPprof() metrics server exposes heap dumps and 30-second CPU profiles — an information-disclosure and DoS surface — so loopback is the safe default.

If you scrape a standalone metrics server over the network today, it will stop being reachable after this upgrade. Restore the previous behaviour explicitly:

// all interfaces (previous default)
srv := metricsserver.New(reg, metricsserver.WithBindAddress(""))
// or a specific address
srv := metricsserver.New(reg, metricsserver.WithHost("10.0.0.5"))

Prefer keeping the loopback default and scraping via a sidecar or an SSH/localhost tunnel where possible.

The managed HTTP and gRPC servers (items above) keep the all-interfaces default for compatibility; only the standalone metrics server changes its default.

3. An out-of-range explicit port is now a hard error

Previously, passing an out-of-range port to the pkg/http adapters (NewServerFromReader / RegisterFromReader) via WithPort short-circuited to an empty settings value — an OS-assigned ephemeral port (:0) — so a typo'd port produced a "healthy" server on an unpredictable port nobody could find. The adapter now forwards the value to the transport constructor's validation, which rejects it:

// before: returned a server bound to a random ephemeral port
// after:  returns a descriptive error
srv, err := gtbhttp.NewServerFromReader(ctx, cfg, handler, gtbhttp.WithPort(70000))
// err: "http: invalid port 70000 (must be 0-65535)"

Action: none for valid ports. If you relied on an out-of-range port silently becoming an ephemeral bind, pass WithPort(0) explicitly to request an ephemeral port.

4. Unsupported adapter options no longer vanish silently

The pkg/http and pkg/grpc server adapters previously ignored any option value outside their accepted families — so a mis-targeted interceptor/middleware chain could be dropped, leaving a server unconfigured. Constructors that return an error (NewServerFromReader) now error naming the rejected concrete type; surfaces without an error return (StartFromReader) log a WARN. Passing only supported option types is unaffected.