Skip to main content

Plan: the install verifies the IngressRoute was created, not that it routes

IMPLEMENTATION RULES: Before implementing this plan, read and follow:

Status: Backlog

Goal: an install must not report a working URL for a route that cannot serve a request.

Found: 2026-09-11 by imac during acceptance, reported by ops-dev (urb-agents#726 finding 5). Measured on a real install, not read off the code.


What it reported, and what was true

route created:    traefik.io/v1alpha1       HostRegexp(...)      <- v3 group, v3 syntax
traefik running: 2.10.5 traefik.containo.us <- v2 group, EMPTY
curl -H 'Host: api-atlas.localhost' -> 404

The install printed a smoke-test URL and exited 0. 🔴 The object existed, so kubernetes.core.k8s was satisfied; the running Traefik was watching a different API group and never saw it.

⚠️ The v2/v3 mismatch itself is imac's host — a consequence of the k3s reset in #646. The check reporting success is ours. A host can be wrong; a verifier saying "ready" about a route that 404s is the defect.

The precedent is in this repo already

360-test-dagster.yml gets this exact distinction right:

D1. Ask Dagster which code locations actually LOADED

and states the reason in its own summary — "D asks the orchestrator whether locations LOADED — a Deployment that exists" is not the same claim. The ingress check infers from kubectl what the Dagster check refuses to infer.

🔴 The obvious mechanism is not available — check this before designing

The natural analogue is "ask Traefik", via its API:

GET /api/http/routers   ->  does a router exist for this rule?

⚠️ UIS disables it. manifests/003-traefik-config.yaml:

dashboard:
enabled: false # Set to true if you want the dashboard exposed

So an API-based check requires changing platform configuration for every installation in order to verify one application's route. That trade is the first thing to decide, and it is why this plan does not simply say "ask Traefik".

Two candidate mechanisms

A — make a real request through Traefik. From inside the cluster, request the route's own host and assert a response the application would produce (PostgREST serves an OpenAPI document at /), not merely "not a connection error".

  • 🟢 Authoritative: it is the thing the operator will do.
  • ⚠️ Must distinguish Traefik's 404 (no router matched) from the application's own 404, or it trades a false pass for a false fail. A positive assertion on the body is what makes this sound; "not 404" is not enough.

B — compare the API group applied against the group the running Traefik serves. Traefik v2 serves traefik.containo.us, v3 serves traefik.io.

  • 🟢 Cheap, deterministic, and names the cause rather than the symptom.
  • ⚠️ This is inference from kubectl — the very move the Dagster check avoids.

Recommendation: A as the verdict, B as the explanation. A answers does it route; B answers why not, and B alone would have told imac exactly what was wrong in one line.

Scope

Every playbook that applies an IngressRoute makes this claim, not only PostgREST — 045-setup-minio, 034-setup-grafana, 220-setup-argocd, 210-setup-litellm, 641-adm-pgadmin and others all apply traefik.io/v1alpha1 and report success on creation. Fix the shared check once rather than per service.

Acceptance

  • On a host whose Traefik does not serve the applied group, the install fails or warns by name instead of printing a smoke-test URL.
  • The message distinguishes "the route does not route" from "could not check".
  • Verified on a cluster where the mismatch is real — this cannot be accepted from a unit test, because the whole defect is that the object looks correct.