CBA — the exam
The Certified Backstage Associate (CBA) is the CNCF and Linux Foundation's associate-level credential for the software that fronts most internal developer platforms — and it is, deliberately, the odd one out on this entire nine-certification shelf. Every other exam here hands you a cluster's worth of custom resources to reason about: policies, meshes, telemetry pipelines, reconciliation loops. CBA hands you a TypeScript monorepo. Backstage is not infrastructure you install and point a config file at — it's an application you fork, own, build, and ship, and the curriculum says so with unusual honesty: NPM or Yarn, TypeScript compilation, a Docker image you build yourself, and real edits to React. Nothing in the four domains below mentions Kubernetes, clusters, or kubectl. This page is the hub: what the exam actually rewards, the four official domains and weights straight from the CNCF curriculum, a worked catalog entity and a real EntityPage.tsx edit, and what to verify before you register.
Picture a workshop with hundreds of builders. Nobody can find anything — who made this, where's the instruction book, how do I start something new? So somebody builds a front desk: one index of everything in the workshop, a shelf of instruction booklets, and a row of buttons that say "make me a new one." Backstage is that front desk. But here's the twist most badges don't have: the CBA doesn't just test whether you can use the front desk. It tests whether you can build it — rewire its drawers, repaint its signs, bolt on a whole new counter — because the front desk isn't furniture you bought, it's furniture your team built and now has to keep fixing.
What the CBA is, and why it doesn't look like the rest of this shelf
☺ Like you're 10: It's a computer quiz, ninety minutes, about one piece of software you build yourself — not a tool you point at a cluster and configure.
CBA is knowledge-based: multiple choice, delivered online, remote-proctored, with no live cluster and no terminal in sight — in that format alone it matches every other associate exam on this ladder. What's different is the subject. CGOA examines a specification. CAPA, CCA, KCA and the rest examine a piece of infrastructure you deploy into a cluster and configure through CRDs. CBA examines one application you personally own the source code of. Backstage ships as a CLI that scaffolds a Yarn workspace, not a Helm chart you values-override and forget. You compile it, you containerize it, you decide what its screens look like.
The single fact everything else on this page derives from: Backstage is a framework, not a product. You do not "install Backstage" the way you install Cilium or Kyverno. You run create-app, and from that second onward the resulting monorepo is yours — its build, its database, its plugins, its upgrades. Every one of the four CBA domains is really just one consequence of that fact, examined from a different angle.
Who tends to sit it: platform engineers who own, or are about to own, a portal repository and want the formal edges of what they've been assembling by trial and error; developer-experience and IDP teams building golden paths, where the scaffolder template button genuinely is the product; and — unusually for this shelf — full-stack or frontend engineers moving into platform work, for whom React and TypeScript are a head start rather than a gap to close. If your organization runs a commercial portal or a distribution you never touch the monorepo of, most of the Development Workflow and Customizing weight below won't apply to your day job, and it's worth being honest about that before you register.
The four official domains and their weights
☺ Like you're 10: Four sections on the test, and the biggest one — nearly a third — is literally changing the front desk's own code.
Everything below is the CNCF's published Certified Backstage Associate (CBA) Exam Curriculum — domain names, percentages and competency lists exactly as printed, not a paraphrase. Four domains, nineteen competencies, summing to exactly 100% (32 + 24 + 22 + 22). Bars are drawn to scale against the largest domain:
Every competency, domain by domain
| Domain | Weight | Competencies (as published) |
|---|---|---|
| Customizing Backstage | 32% | Understand frontend versus backend plugins · Customizing Backstage plugins · Make changes to React code in Backstage App · Using Material UI components |
| Backstage Development Workflow | 24% | Build and run Backstage projects locally · Understand local development workflows · Compile a Backstage project with TypeScript · Download and install dependencies for a Backstage project with NPM/Yarn · Use Docker to build a container image of a Backstage project |
| Backstage Infrastructure | 22% | Understand the Backstage framework · Configure Backstage · Deploy Backstage to production · Understand Backstage client-server architecture |
| Backstage Catalog | 22% | Understand how/why to use Backstage Catalog · Populate Backstage Catalog · Using annotations · Working with manually registered entity locations · Troubleshooting entity ingestion · Working with automated ingestion |
Reading the shape of this blueprint
Three things should change how you study, and none of them are obvious from the domain names alone.
First: 56% of this exam is software development. Customizing (32%) plus Development Workflow (24%) is a clear majority, and between them they name TypeScript, NPM/Yarn, Docker, React and Material UI explicitly. If your instinct is to prepare the way you'd prepare for CGOA — reading definitions until they stick — you're preparing for the smaller half of this particular paper.
Second: the catalog is tied for smallest, at 22% — the same as Infrastructure, well behind Customizing — which surprises almost everyone, because the catalog is what people actually talk about when they talk about Backstage. Notice how operational its competencies read: two of its six are troubleshooting and manual registration, the domain of someone who has debugged a missing service, not someone who has read the marketing page.
Third: nothing here is Kubernetes. "Deploy Backstage to production" is a named competency, and in most real shops that deployment lands on a cluster — but the curriculum's stated subject is the Backstage project itself, not the substrate underneath it. Don't assume the Kubestronaut foundation you already hold carries you through this one; it barely applies here.
The development workflow — 24%
☺ Like you're 10: Four commands you should be able to run half-asleep — make it, install it, run it, and pack it into a box a server can run.
This domain is muscle memory for anyone who's lived in a Node monorepo. npx @backstage/create-app@latest produces a Yarn workspace containing packages/app (the React frontend) and packages/backend (the Node backend), plus a root app-config.yaml. yarn install resolves the whole workspace; yarn dev runs both halves with hot reload — frontend on :3000, backend on :7007. Because the entire thing is TypeScript, yarn tsc is a real gate, not a linter suggestion: a type error stops the build cold.
# Scaffold a new portal — this monorepo is now yours, you didn't "install" it npx @backstage/create-app@latest --path mission-portal cd mission-portal yarn install # resolves the whole Yarn workspace yarn dev # app on :3000, backend on :7007, both hot-reloading # Run one half at a time — the client-server split made concrete yarn workspace app start yarn workspace backend start # Add a plugin: most ship as a pair, frontend AND its backend counterpart yarn workspace app add @backstage/plugin-kubernetes yarn workspace backend add @backstage/plugin-kubernetes-backend # Type-check, lint and test the whole workspace before you trust it yarn tsc && yarn lint:all && yarn test:all # Production build, then a container image from the BACKEND Dockerfile yarn build:all yarn build:backend --config ../../app-config.yaml docker image build . -f packages/backend/Dockerfile --tag kubestronaut/mission-portal:2026.08.1 # Keep every @backstage/* package on one release line npx @backstage/cli versions:bump
Two details there are worth marks on their own. The image is built from packages/backend/Dockerfile, not from two separate Dockerfiles, because in a production build the compiled frontend is served by the backend — one image, one process, not a frontend container talking to a backend container. And versions:bump is the whole upgrade story: Backstage releases often, its packages must move together, and a portal left untouched for a year is a genuinely painful thing to bring forward again.
Backstage infrastructure — the framework and the client-server split — 22%
☺ Like you're 10: One half of the front desk is what visitors see and touch. The other half is locked in the back room, and it's the only half allowed to hold a key.
"Understand Backstage client-server architecture" means being able to say what runs where, cold. The frontend is a React single-page app — it holds no secrets and only ever talks to the backend over HTTP, because anything shipped to a browser is public by definition. The backend is a Node service that owns the database connection, the credentials for GitHub and every cluster, and every plugin route that reaches a third-party system. Between them sits the catalog, backed by a real database — PostgreSQL in production; SQLite is development-only and forgets everything the moment the process restarts. Configuration is one layered app-config.yaml, with local and production overrides stacked on top and ${ENV_VAR} substitution throughout.
app:
baseUrl: https://portal.kubestronaut.example # where the FRONTEND is served
backend:
baseUrl: https://portal.kubestronaut.example # where the API answers
listen: { port: 7007 }
database:
client: pg # SQLite is DEV ONLY — no persistence
connection:
host: ${POSTGRES_HOST}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
integrations:
github:
- host: github.com
apps:
- $include: github-app-credentials.yaml # a GitHub App, never a personal token
catalog:
rules:
# omit Template here and your golden-path templates silently refuse to appear
- allow: [Component, API, Resource, System, Domain, Group, User, Location, Template]
locations: # STATIC registration, declared right here
- type: url
target: https://github.com/kubestronaut/platform-config/blob/main/catalog/org.yaml
providers: # AUTOMATED ingestion
github:
missionOrg:
organization: kubestronaut
catalogPath: /catalog-info.yaml
filters: { branch: main, repository: '.*' }
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }"Deploy Backstage to production" is where the honesty of that diagram bites. It means a real database, real identity — an auth provider plus a resolver that maps a signed-in person to a User entity — secrets injected at runtime rather than committed, and a pipeline that produces that backend image on every change. The portal is a production service with an on-call owner, not a laptop with the lid open.
The catalog — 22%
☺ Like you're 10: The index at the front desk is one big list of cards. Each card has a name, an owner, and a few notes — and there are exactly three ways a card gets added to the stack.
The catalog is a graph of entities, each a YAML document with a Kubernetes-shaped envelope — apiVersion, kind, metadata, spec — under backstage.io/v1alpha1. Know the kinds: Component, API, Resource, System, Domain, Group, User, Location, Template. The file is conventionally catalog-info.yaml at the root of the repo it describes — that's the whole trick, since the description ships with the code and therefore stays true.
# catalog-info.yaml — lives at the ROOT of the checkout of mission-log's own repo
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: mission-log
description: Records every reconciliation event across the fleet.
annotations:
backstage.io/techdocs-ref: dir:. # lights up the Docs tab
backstage.io/kubernetes-id: mission-log # wires the Kubernetes tab to real pods
argocd/app-name: mission-log-prod # surfaces sync status from Argo CD
spec:
type: service
lifecycle: production
owner: group:default/platform-team # derives ownedBy / ownerOf
system: telemetry # derives partOf / hasPart
providesApis: [mission-log-api]
dependsOn:
- resource:default/mission-log-dbAnnotations are their own named competency: free-form key/value pairs under metadata.annotations that individual plugins look for. Two are set for you and matter when debugging: backstage.io/managed-by-location (which file produced this entity) and backstage.io/orphan (its parent location is gone).
The curriculum names the two ends of the range explicitly, and in practice there are three routes worth telling apart. Manually registered entity locations — a human pastes a URL into "Register existing component," creating a Location. Static locations — URLs listed under catalog.locations in config, good for org data and templates. Automated ingestion — entity providers that crawl a whole GitHub or GitLab organization on a schedule and, crucially, remove entities when the file disappears. Only providers scale past a handful of services.
Troubleshooting entity ingestion separates readers from operators. Three failure modes recur: a malformed or unreachable catalog-info.yaml shows a processing error on the entity page and the entity silently stops refreshing; an orphan lingers when its parent location was removed; and a dangling owner reference points at a Group that was never ingested, so ownership links go nowhere — ingest org data first. Everything underneath is a plain REST API:
# What does the catalog actually think exists? curl -s -H "Authorization: Bearer $TOKEN" \ "$BASE/api/catalog/entities/by-name/component/default/mission-log" \ | jq '.metadata.annotations' # Ghost service on the page? Delete the LOCATION that created it, not the entity curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/catalog/locations" | jq . curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$BASE/api/catalog/locations/<id>"
Customizing Backstage — the largest domain, 32%
☺ Like you're 10: This is the part where you actually pick up a screwdriver and change the front desk — repaint a sign, add a drawer, wire in a new button.
The domain that catches operations-minded candidates out. Start with the distinction the curriculum names first: frontend versus backend plugins. A frontend plugin is React — pages, cards, tabs — installed into packages/app and rendered in the browser. A backend plugin is Node: it holds credentials, talks to external systems, and exposes HTTP routes, installed into packages/backend. Many plugins ship as a pair, and "I installed the plugin but the tab is empty" is almost always "you installed one half."
| Aspect | Frontend plugin | Backend plugin |
|---|---|---|
| Runs in | The browser (React SPA) | The Node backend service |
| Installed into | packages/app | packages/backend |
| Written with | React + TypeScript + Material UI | TypeScript + Express-style routers |
| May hold secrets | No — anything shipped to the browser is public | Yes — tokens, GitHub App credentials, cluster service accounts |
| Typical job | A tab, a card, a page on the entity view | Fetch a third-party API, persist, expose /api/<plugin> |
| Wired up in | App.tsx, EntityPage.tsx | packages/backend/src/index.ts |
"Make changes to React code in Backstage App" means editing the two files every portal owner learns by heart: App.tsx declares routes and top-level structure, and EntityPage.tsx decides which tabs and cards appear for each kind and type of entity. "Using Material UI components" means the layout primitives — Grid, Card, Typography, Button — because Backstage's own component library sits on top of Material UI, and a custom card that ignores the grid looks visibly broken next to everything around it.
// packages/app/src/components/catalog/EntityPage.tsx
import { Grid } from '@material-ui/core';
import { EntityLayout, EntityAboutCard, EntityLinksCard } from '@backstage/plugin-catalog';
import { EntityKubernetesContent } from '@backstage/plugin-kubernetes';
const serviceEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<Grid container spacing={3} alignItems="stretch">
<Grid item md={6}><EntityAboutCard variant="gridItem" /></Grid>
<Grid item md={6}><EntityLinksCard /></Grid>
</Grid>
</EntityLayout.Route>
{/* one new EntityLayout.Route — this block IS "customizing Backstage" */}
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
<EntityKubernetesContent refreshIntervalMs={30000} />
</EntityLayout.Route>
</EntityLayout>
);The backend half is wired separately, and recent Backstage versions use the new backend system, where each plugin is added to a backend instance rather than assembled by hand — know this shape, because it's what a current create-app actually produces:
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-github')); // automated ingestion
backend.add(import('@backstage/plugin-techdocs-backend'));
backend.add(import('@backstage/plugin-kubernetes-backend')); // the other half of the tab
backend.add(import('@backstage/plugin-scaffolder-backend')); // golden-path templates
backend.start();To create repositories, grant team access and open pull requests, the scaffolder backend holds real credentials — ideally a scoped GitHub App, never a personal access token tied to one employee. Keep templates in a repository only the platform team can merge to, restrict what may be ingested with catalog.rules, and inject the secret at runtime rather than baking it into config. A portal that can create infrastructure is a production security boundary, the same way KCA's admission policies are.
"Before the portal, starting a service meant four tickets and a week of asking around. Now I click New Service (Golden Path), fill in a form, and there's a repo, a pipeline, and a namespace. I don't care that it's React underneath — I care that the owner listed on the page is right, because when something breaks at 2am the catalog is how I find out who to wake."
Exam logistics — and how to verify them
☺ Like you're 10: Some facts about the test don't change; the price and timing do, all the time. Always check the official page before you pay for anything.
This is an independent, unofficial study resource — not affiliated with the CNCF or the Linux Foundation. Some facts about the CBA are structural and safe to state; others are exactly the sort the Linux Foundation revises without announcement. This table separates the two deliberately, checked in 2026.
| Item | Detail |
|---|---|
| Full name | Certified Backstage Associate (CBA) |
| Provider | CNCF & The Linux Foundation |
| Level | Associate — alongside CGOA, CAPA and the rest of this course's shelf |
| Format | Online, proctored, multiple-choice. Knowledge-based: no cluster, no terminal, no live grading of code you write during the exam — the development skills it names are examined by asking about them, not by watching you type |
| Duration | 90 minutes |
| Delivery | Remote-proctored from your own machine: system check, webcam room scan, government-issued photo ID matching your registration |
| Price | US$250 for the exam alone, including one retake. Bundles with training are priced separately, and CNCF discount codes are common enough that the sticker price often isn't what people pay — see Voucher & Discount Stacking |
| Eligibility window | 12 months from purchase in which to sit the exam |
| Certification validity | 2 years from the date you pass |
| Prerequisites | None. No prior certification is required, and CBA is not required for anything else on the ladder |
| Subject | One project: Backstage — the framework, its monorepo, its catalog, its plugins |
| Blueprint | Four weighted domains, 19 competencies summing to 100% — as tabulated above |
| Question count | Not published. Treat any specific figure you read for it elsewhere as folklore and plan for a full 90-minute paper you can't rush |
| Pass mark | 75%, per the Linux Foundation's general Multiple Choice Exam FAQ — not restated on the CBA page itself, but it applies to every LF multiple-choice exam, CBA included |
Price, duration, retake terms, eligibility and validity windows, proctoring rules, and even domain weights are all figures the Linux Foundation and CNCF revise without much notice. Before you register or pay for anything, read the current official Linux Foundation CBA page and the candidate handbook yourself, end to end. If anything on this page disagrees with them, they are right and this page is stale.
Don't confuse the eligibility window — how long you have to sit the exam after buying it — with the certification validity, how long the credential lasts after you pass. At the time of writing those are 12 months and 2 years respectively. Confirm both on the official page before you register.
↗ Official CBA page — Linux Foundation ◆ CNCF certification page ◆ Official CNCF curriculum repository ◆ Backstage documentation
Where it sits — and what to do next
☺ Like you're 10: This is a specialist badge for one tool, not a ladder rung anything else depends on. From here you either drill it directly, or go build a real portal to make the vocabulary stick.
CBA gates nothing on the sixteen-exam ladder and nothing gates it — see The Sixteen-Exam Ladder for how it fits next to the other eight project associates this course covers. Continue with the CBA study plan, which maps every domain above to a pacing schedule, then drill with the practice question bank and two timed papers — Mock Exam · Set 1 and Mock Exam · Set 2. For the conceptual model underneath the whole exam, read Backstage and The Backstage Portal Model; for hands-on practice with the failure mode this domain likes most, run the missing-catalog-entity drill and, further along, the capstone's portal stage.
CBA's biggest payoff outside this ladder is the overlap with Platform Engineering's CNPE: Backstage sits under that exam's Platform APIs & Self-Service domain, where the portal is the storefront in front of self-service and platform architecture — but the CBA goes much deeper into one product than that exam requires, and most of its weight sits on skills the CNPE never tests at all. If a broader look at where Backstage fits a platform-engineering role is your actual goal, the fuller comparison lives on Platform Engineering's own CBA page, and Developer experience covers what customization is for.
Nothing on this exam survives contact with theory alone. Run npx @backstage/create-app@latest --path cba-lab, then yarn install && yarn dev. Now do four things and name the competency each one exercises: (1) add a catalog-info.yaml to any repo of yours and register it by URL — manually registered entity locations; (2) delete a required field from it and watch the entity page show a processing error — troubleshooting entity ingestion; (3) open EntityPage.tsx, add an EntityLayout.Route with a Material UI Grid inside, and watch it hot-reload — React code and Material UI components; (4) run yarn build:backend and docker image build -f packages/backend/Dockerfile . — Docker container image. Forty-five minutes, all four domains.
Mira: Another badge for the ladder — this one's mine. Fair warning: it isn't a Kubernetes exam.
Foxy: Not Kubernetes? Then what's left, more YAML?
Mira: Some YAML. Mostly React, TypeScript, and a Dockerfile you build yourself.
Nutty: Nineteen competencies, four domains — and I counted, thirteen of them are code or a build step!
Benny: Which is why I stopped reading about it and just ran yarn dev on a fresh app. Twenty minutes in, half the vocabulary already made sense on its own.
Gizmo: Or skip the Docker step forever — yarn dev runs fine on your laptop, ship that instead. Nobody restarts pods anyway. 😈
Timmy: yarn dev isn't a deployment, Gizmo. "Deploy Backstage to production" is a named competency — it means Postgres, a real auth provider, and an image built from packages/backend/Dockerfile, not a laptop with the lid open.
1. Name the four CBA domains and their weights, and which is largest. 2. What's the single biggest structural difference between CBA and the other eight project associates on this shelf? 3. What's the difference between a frontend and a backend Backstage plugin, and which one may hold secrets? 4. Name the three ways an entity gets into the catalog, and which one scales to a whole organization. 5. Why is SQLite unacceptable for a production Backstage deployment, and what replaces it? 6. Which two files does "make changes to React code in Backstage App" mainly point at? 7. Is the CBA hands-on or multiple choice, and where is its pass mark actually published?
Check your answers
- Customizing Backstage 32%; Backstage Development Workflow 24%; Backstage Infrastructure 22%; Backstage Catalog 22%. Customizing is the largest, at nearly a third of the paper.
- Every other associate exam on this shelf examines infrastructure you deploy into a cluster and configure through CRDs. CBA examines one application you personally own the source code of — a TypeScript/React monorepo you build, compile and containerize yourself, not a Helm chart you values-override.
- A frontend plugin is React, installed into
packages/app, and runs in the browser — it must never hold secrets, since anything shipped to the browser is public. A backend plugin is Node, installed intopackages/backend, and holds credentials, database access and HTTP routes. Many plugins ship as a pair, and installing only one half is why a tab renders empty. - Manual registration (a human registers a URL, creating a
Location), static locations (URLs undercatalog.locationsin config), and automated ingestion via entity providers that crawl an organization on a schedule. Only providers scale — and they also remove entities when the file disappears. - SQLite is development-only — it does not survive a process restart, so the catalog vanishes with it. Production uses PostgreSQL, configured under
backend.databasewith credentials injected from the environment rather than committed. App.tsx(routes and top-level structure) andEntityPage.tsx(which tabs and cards appear for each kind and type of entity).- Multiple choice — an online, remotely proctored, knowledge-based paper, like every CNCF associate exam. The pass mark is published, just not on the CBA page itself: the Linux Foundation's Multiple Choice Exam FAQ requires a score of 75% or above across every LF multiple-choice exam, CBA included. Confirm current figures on the official Linux Foundation CBA page before you register.