Score's own pitch, repeated across its docs and blog, is "one YAML to rule them all" — write a single score.yaml declaring what a service needs, and a CLI translates it into whatever your target actually runs: docker-compose.yaml for local dev, raw Kubernetes manifests for a cluster, Helm values for a chart. CNCF calls the category "the invisible platform" — a layer where developer intent and operator implementation never have to touch the same file.
That's a genuinely useful idea for a git-push PaaS like bex, which is exactly the kind of platform Score is aimed at: today bex asks a developer to describe their app in bex's own manifest format, the same tradeoff every PaaS with a proprietary config schema makes. So here's the concrete question, worked through rather than waved at: should bex drop its own format and target score.yaml instead?
The short answer is no, not as the core manifest — but yes, as an import path. Score's abstraction is real and its provisioner model is the right idea, but the spec's own limitations mean a Cluster-API-native platform can't run its actual primitives through it. What follows is the schema, the mechanism, the named gaps, and why that specific split — not "adopt" or "ignore" — is the right call.
What Score actually is
A score.yaml is a platform-agnostic description of one workload. Here's a real example, adapted from the spec's own reference:
apiVersion: score.dev/v1b1
metadata:
name: sample
containers:
main:
image: ghcr.io/score-spec/sample-app:sha-2533037
variables:
PG_CONNECTION_STRING: "postgresql://${resources.db.username}:${resources.db.password}@${resources.db.host}:${resources.db.port}/${resources.db.database}?sslmode=disable"
service:
ports:
web:
port: 8080
resources:
db:
type: postgres
dns:
type: dns
route:
type: route
params:
host: ${resources.dns.host}
path: /
port: 8080Three sections do the real work. containers is the familiar part — image, command, env vars, resource limits, probes. service.ports declares what the workload listens on. resources is the interesting one: type: postgres doesn't say where that Postgres comes from — a local container, a CloudNativePG instance, a managed RDS database — it just declares the dependency exists, and other fields reference it back via ${resources.db.host}-style placeholders that get resolved at generation time, not authoring time. A developer writes "I need a database" once; what "database" concretely means is somebody else's problem, deferred to whoever runs the CLI against a specific target.
The provisioner model is what makes "portable" true
That deferral works because of provisioners — the pluggable mapping layer between an abstract type: postgres and a concrete implementation. score-compose resolves the same score.yaml into a docker-compose.yaml with a Postgres container wired in for local dev. score-k8s resolves it into Kubernetes manifests, where the provisioner config loaded for that environment decides whether db becomes a CloudNativePG cluster, a Crossplane-managed RDS claim, or a sidecar container — the score.yaml itself never changes across any of those targets. (score-helm, notably, is already deprecated in favor of score-compose/score-k8s — a signal that even Score's own maintainers are consolidating around fewer, more capable implementations rather than one per target format.)
This is the actual value proposition, and it's real: one manifest, several provisioners, zero rewrites when the target changes. It's also exactly the kind of tooling a small platform team doesn't want to build itself — a docker-compose generator and a Kubernetes-manifest generator that already exist, tested, in the open, is worth something.
Where the project actually stands
Score joined CNCF as a Sandbox project in mid-2024 and is still at Sandbox level — the entry tier, below Incubation and Graduation, where a project sits until it demonstrates broader adoption and a wider maintainer base than its founding company. That founding company is Humanitec, which still actively develops Score alongside its own Platform Orchestrator product. Score keeps showing up at KubeCon: a maintainer summit and a joint session on bridging inner/outer developer loops both landed on the KubeCon EU 2026 schedule in Amsterdam, and a KubeCon India 2026 talk pairs Score with Crossplane specifically on the composition question this post is asking about bex.
What's conspicuously absent from any of that is a hard adoption number — no public count of companies or production installs, which is itself informative. Compare that to Helm, which backs an ecosystem of 10,000+ published charts covering nearly every common workload shape a team might deploy. Score's own blog is upfront that this is a different kind of project: a specification other tools implement against, not a chart repository — closer in spirit to how OpenAPI standardizes request/response shapes without shipping the servers that implement them. That comparison is fair on the abstraction layer, but it also means Score inherits OpenAPI's dependency on other people actually building against it, and two years into Sandbox status, "other people building against it" is still mostly Score's own score-compose and score-k8s CLIs rather than a broad third-party ecosystem the way OpenAPI has one.
What a 2026 field review says it can't do yet
The honest ceiling shows up fast once you look past the pitch. A widely-cited 2026 developer-experience review of Score put it bluntly: "a compelling concept with a thin implementation." The named gaps are specific, not hand-wavy — the review lists init containers, sidecars, custom RBAC, pod anti-affinity rules, spot-instance tolerations, and topology spread constraints as things "Score can't express." On top of that, there's no operational lifecycle: no versioning, no rollback, nothing Helm's release model gives you for free. And resources.db.type: postgres is deliberately ambiguous about which Postgres you get — RDS versus CloudNativePG versus a sidecar container is entirely a provisioner decision, which the reviewer flags as underdeveloped rather than merely "flexible." The verdict: fine for piloting a simple service, "not suitable for platform-wide bets" yet.
Score's own FAQ doesn't dispute this — it explicitly scopes the project as "tightly scoped," not "a fully featured YAML replacement for any platform." That's the spec being honest about its own boundary, which is more useful than a vendor claiming completeness it doesn't have.
Running bex's actual primitives through score.yaml
Here's where the review's abstract gap list becomes concrete for a platform built on Cluster API rather than a generic Kubernetes cluster. bex's manifest has to express things that are specific to how a Cluster-API-managed fleet on owned Hetzner hardware works, not just "what does this container need":
| bex primitive | Expressible in score.yaml today? |
|---|---|
| Container image, env vars, ports, resource limits | Yes — containers and service.ports cover this directly |
| "I need a Postgres" as an abstract dependency | Yes — resources.db.type: postgres, resolved by a provisioner |
| Which node pool a workload's Machine should schedule onto | No field — Score has no concept of a Cluster API MachinePool or node-class selection |
| Per-tenant custom domain routed through Gateway API | No — resources.type: route exists but has no notion of tenant-scoped Gateway API HTTPRoute ownership or per-tenant TLS |
| Autoscaling policy tied to bex's own metrics | No — nothing in the spec addresses HPA/KEDA-style scaling triggers |
| Deploy/rollback lifecycle, deploy history | No — Score is explicitly scoped out of this; it only describes the workload, not its release history |
That's not a knock on Score — it's a knock on trying to make a narrowly-scoped workload spec carry a platform's own differentiated primitives. The resources block is genuinely good at the thing it's good at (abstracting "I need a database" away from a specific implementation). It was never designed to carry "which Hetzner node pool" or "which tenant owns this domain," and stretching it to do so would mean either forking the spec or bolting on non-standard extensions — at which point you're not really "targeting the open standard" anymore, you're maintaining a fork with extra steps.
The actual call: import path, not core manifest
So the decision splits cleanly once the table above is in front of you. Don't replace bex's own manifest — which already has first-class fields for node pools, per-tenant Gateway API domains, and deploy lifecycle — with a spec that structurally can't express any of those three things yet. But do build a score.yaml → bex manifest import path. That gets bex the actual portability win Score is chasing — a team migrating off Railway, Fly, or a bare Compose file with an existing score.yaml (or willing to write one) gets a zero-friction on-ramp — without asking bex's control plane to run its differentiated primitives through a spec that admits, in its own FAQ, that it isn't trying to cover them.
That's the "invisible platform" framing from Score's own pitch, applied honestly: the platform a developer's intent disappears into should still be the one that actually owns the node pools, the domains, and the rollback history — Score just becomes the format that gets a new team's intent into that platform in the first place, not the format the platform runs on internally.
What would change the answer
Three things would move this from "import path" to "worth reconsidering as the core format": the provisioner ecosystem maturing well past the two official implementations that exist today, Score graduating past CNCF Sandbox toward Incubation (a real signal of a broader maintainer base and production hardening, not just a good idea), and the new experimental score-radius implementation showing that Score can compose with a project — Radius — that already handles infra lifecycle and recipes rather than staying workload-only. None of those are close yet. Until they are, score.yaml is a good front door and a poor foundation.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



