Retries¶
Coxswain retries failed upstream attempts against another endpoint in the same backend group. Retrying protects clients from transient upstream failures — a refused connection, a connect timeout, or a retriable response from one pod — without surfacing the error.
Both bindings reference the same RetryPolicy custom resource, resolved through the identical spec→config translation — parity between the two surfaces is guaranteed by construction, not by convention:
| Binding | When to use |
|---|---|
| Ingress annotation | Per-Ingress (HTTP only): ingress.coxswain-labs.dev/retry: "namespace/name" |
Gateway API ExtensionRef |
Per-HTTPRoute / GRPCRoute rule, via the same RetryPolicy custom resource |
The configuration model mirrors Gateway API GEP-1731 (attempts / backoff / codes) so that, once GEP-1731 graduates to the Standard channel, the HTTP surface can be moved to the native HTTPRoute.spec.rules[].retry field with no behavioural change. gRPC retry has no GEP-1731 equivalent and stays on the RetryPolicy CR permanently.
Model¶
attempts— the number of additional attempts after the first, and the gate: whenattemptsis0(or absent on an Ingress), retrying is disabled entirely. Withattempts: 2, Coxswain makes up to 3 total attempts.- Connection failures and connect-timeouts are always retried when
attempts >= 1. They are safe to replay — no request bytes reached the upstream. There is no separate opt-in (this matches GEP-1731, where connection-error retries are implicit). codesselects which upstream responses retry. See HTTP and gRPC below.backoff— a minimum delay before each retried attempt. Applied as a fixed minimum; exponential backoff and jitter are not yet applied.
Replay guard: response retries require the request body to be buffered. Requests whose bodies are too large or were only partially received cannot be retried and pass through to the client as-is.
Each retry increments coxswain_proxy_upstream_retries_total{condition=...} (connect-failure, timeout, http-code, grpc-code).
HTTP codes¶
Retries fire when the upstream response status is in the code set. When omitted, the set defaults to 502, 503, 504 — the "gateway could not obtain a processed response" codes, where the request almost certainly did not execute, so a retry is safe. 500 is deliberately excluded from the default: the application ran, and retrying risks double execution. An explicit empty set opts out of response-code retries (connection/timeout retries still apply).
gRPC codes¶
GRPCRoute retries key on the grpc-status code, not the HTTP status (a gRPC response is HTTP 200 even on RPC failure). A gRPC response is only retriable when the status arrives trailers-only — i.e. the grpc-status rides in the response headers with nothing streamed yet. A grpc-status delivered as a trailer after a message is not retriable (the response is already in flight), matching Envoy's behaviour.
When omitted, grpcCodes defaults to [14] (UNAVAILABLE). A trailers-only UNAVAILABLE implies the RPC never executed, so retrying is safe even without idempotency metadata. DEADLINE_EXCEEDED (4) and RESOURCE_EXHAUSTED (8) are excluded from the default — retrying them compounds latency or worsens overload. An explicit empty set opts out.
Ingress annotation¶
Ingress is HTTP-only. Reference a RetryPolicy CR — the same one an HTTPRoute ExtensionRef filter would point at — from the retry annotation:
apiVersion: gateway.coxswain-labs.dev/v1alpha1
kind: RetryPolicy
metadata:
name: resilient
namespace: shop
spec:
attempts: 2
codes: [503, 504] # optional; default [502, 503, 504]
backoff: 100ms # optional
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: shop
annotations:
ingress.coxswain-labs.dev/retry: "shop/resilient"
Fail-open: if the referenced RetryPolicy CR is missing, the route serves with retrying disabled (a WARN is logged) rather than failing the route — the same posture as the Gateway API binding below.
Migration from the inline retry-attempts/retry-codes/retry-backoff annotations (breaking)
Earlier releases exposed three inline annotations — retry-attempts, retry-codes, retry-backoff — duplicating the RetryPolicy CRD schema. These are removed; move each Ingress's values into a RetryPolicy CR and point retry at it.
See the Ingress annotations reference for the full annotation semantics.
Gateway API ExtensionRef¶
Define a RetryPolicy custom resource and reference it from an HTTPRoute or GRPCRoute rule's filters with type: ExtensionRef.
apiVersion: gateway.coxswain-labs.dev/v1alpha1
kind: RetryPolicy
metadata:
name: resilient
namespace: shop
spec:
attempts: 3
backoff: 100ms
codes: [503, 504] # optional; default [502, 503, 504]
grpcCodes: [14] # optional; default [14] (UNAVAILABLE); GRPCRoute only
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop
namespace: shop
spec:
parentRefs:
- name: public
rules:
- backendRefs:
- name: shop
port: 8080
filters:
- type: ExtensionRef
extensionRef:
group: gateway.coxswain-labs.dev
kind: RetryPolicy
name: resilient
The same CR works on a GRPCRoute rule — the grpcCodes field then governs which grpc-status outcomes retry. codes still applies on a GRPCRoute for transport-level HTTP errors.
Fail-open: if the referenced RetryPolicy CR is missing, the route serves with retrying disabled (a WARN is logged) rather than failing the route.