🤖 Generated by the Daily AI Engineer
Problem
The platform runs Backstage, and it delivers nothing today while charging real operational rent.
It is a demo, by design and by admission. docs/backstage.md labels it "Status: scaffold", and it runs the upstream demo image (ghcr.io/backstage/backstage:1.52.0) with an example catalog and example plugins. The manifest states the reason plainly: the Kubernetes plugin and GitHub catalog discovery are "compiled in at build time and CANNOT be enabled through values". So the thing that makes a portal a portal — a real catalog of our services — is simply not there, and cannot be switched on.
The only documented path to value is owning a codebase. docs/backstage.md → Productionisation requires:
- scaffolding a new
devantler-tech/backstage repo with @backstage/create-app — a TypeScript monorepo we own, build, and upgrade forever;
- publishing our own image via
publish-app.yaml;
- a Dex static OIDC client + sign-in resolver (replacing the forward-auth transport);
- a read-only Kubernetes ServiceAccount/ClusterRole, a GitHub App, and an
api.github.com netpol egress.
That is four workstreams and a permanent maintenance surface before the portal does anything useful.
Meanwhile it already costs us: a CloudNativePG database (2 instances, synchronous, Longhorn), forward-auth SSO, an HTTPRoute, a CiliumNetworkPolicy, and VPA interplay. On 2026-07-14 a routine rolling node reboot put it into CrashLoopBackOff at 17 restarts, 0/1 ready — it could not survive a cold start (a 30s startup budget against a ~60s boot; fixed in #2636). It was only ever "healthy" because nothing had restarted it.
Net: all cost, no benefit, and the way out is the expensive way.
Proposed direction
Adopt Port on its Free tier and decommission Backstage.
Port is a hosted internal developer portal. Its Free tier is $0 — up to 15 seats, 10,000 catalog entities, 500 automation runs, full platform, no card (pricing). We have ~14 repos and a handful of services, so we would use a rounding error of that quota, indefinitely. Crucially it is configuration-driven, not compile-time: the catalog, scorecards and self-service actions we actually want need no owned codebase.
Decommissioning Backstage also removes infrastructure rather than adding it — the CNPG database, the forward-auth wiring, the netpol, the route and the crash-prone deployment all go away.
The trade-off, stated explicitly
This platform is deliberately SaaS-free: Coroot was chosen precisely as "Community Edition, fully self-hosted — no SaaS tier, no remote-write" (docs/dr/alerting.md), and OpenBao, Longhorn and Velero→R2 follow the same principle. Port would be the first SaaS in the estate, and it is not passive — it needs a Kubernetes exporter with cluster read access and a GitHub App with repository read. Metadata about our repos and cluster topology would leave the estate.
Accepted, with reasons: a portal is not a control plane. If Port disappears, production keeps running — categorically unlike putting secrets or observability in SaaS. And the blast radius is boundable.
Mitigations (part of the definition of done):
- Kubernetes exporter: read-only, least-privilege ClusterRole, no write/exec.
- GitHub App: scoped to public repos +
platform initially. Private tenants (wedding-app, ascoachingogvaner, unifi) are excluded from discovery until we consciously decide otherwise.
- No secrets, no runtime data, no logs — catalog metadata only.
Scope
Each step is independently shippable; split into child issues when work starts.
Rough size
M. Standing Port up is S (config, no code). The decommission is a clean subtraction. The bulk is deciding what the catalog and scorecards should actually say — which is the work Backstage was deferring anyway.
Notes
Problem
The platform runs Backstage, and it delivers nothing today while charging real operational rent.
It is a demo, by design and by admission.
docs/backstage.mdlabels it "Status: scaffold", and it runs the upstream demo image (ghcr.io/backstage/backstage:1.52.0) with an example catalog and example plugins. The manifest states the reason plainly: the Kubernetes plugin and GitHub catalog discovery are "compiled in at build time and CANNOT be enabled through values". So the thing that makes a portal a portal — a real catalog of our services — is simply not there, and cannot be switched on.The only documented path to value is owning a codebase.
docs/backstage.md→ Productionisation requires:devantler-tech/backstagerepo with@backstage/create-app— a TypeScript monorepo we own, build, and upgrade forever;publish-app.yaml;api.github.comnetpol egress.That is four workstreams and a permanent maintenance surface before the portal does anything useful.
Meanwhile it already costs us: a CloudNativePG database (2 instances, synchronous, Longhorn), forward-auth SSO, an HTTPRoute, a CiliumNetworkPolicy, and VPA interplay. On 2026-07-14 a routine rolling node reboot put it into CrashLoopBackOff at 17 restarts, 0/1 ready — it could not survive a cold start (a 30s startup budget against a ~60s boot; fixed in #2636). It was only ever "healthy" because nothing had restarted it.
Net: all cost, no benefit, and the way out is the expensive way.
Proposed direction
Adopt Port on its Free tier and decommission Backstage.
Port is a hosted internal developer portal. Its Free tier is $0 — up to 15 seats, 10,000 catalog entities, 500 automation runs, full platform, no card (pricing). We have ~14 repos and a handful of services, so we would use a rounding error of that quota, indefinitely. Crucially it is configuration-driven, not compile-time: the catalog, scorecards and self-service actions we actually want need no owned codebase.
Decommissioning Backstage also removes infrastructure rather than adding it — the CNPG database, the forward-auth wiring, the netpol, the route and the crash-prone deployment all go away.
The trade-off, stated explicitly
This platform is deliberately SaaS-free: Coroot was chosen precisely as "Community Edition, fully self-hosted — no SaaS tier, no remote-write" (
docs/dr/alerting.md), and OpenBao, Longhorn and Velero→R2 follow the same principle. Port would be the first SaaS in the estate, and it is not passive — it needs a Kubernetes exporter with cluster read access and a GitHub App with repository read. Metadata about our repos and cluster topology would leave the estate.Accepted, with reasons: a portal is not a control plane. If Port disappears, production keeps running — categorically unlike putting secrets or observability in SaaS. And the blast radius is boundable.
Mitigations (part of the definition of done):
platforminitially. Private tenants (wedding-app,ascoachingogvaner,unifi) are excluded from discovery until we consciously decide otherwise.Scope
k8s/bases/apps/backstage/(HelmRelease, CNPGbackstage-db,HTTPRoute,CiliumNetworkPolicy, backend-secretPassword+ExternalSecret), its provider/cluster overlay entries, and its Dex/forward-auth wiring. Retire thebackstage.${domain}record.docs/backstage.mdwithdocs/portal.md; updateAGENTS.md/ stack map references.Each step is independently shippable; split into child issues when work starts.
Rough size
M. Standing Port up is S (config, no code). The decommission is a clean subtraction. The bulk is deciding what the catalog and scorecards should actually say — which is the work Backstage was deferring anyway.
Notes