Certifications · CBA

CBA Study Plan

CBA is a 90-minute knowledge exam across four domains, and the four are nowhere near equal: Customizing Backstage is 32% of the paper, and it names React, TypeScript and Material UI by name. Add Backstage Development Workflow at 24% and the two software-development domains together are 56% of the exam — more than half of what you're studying is a JavaScript toolchain, not a portal-administration checklist. This page turns that arithmetic into a fourteen-day schedule, but it deliberately does not study the domains in weight order. It studies them in dependency order: Infrastructure first, so you can name what runs where before anything else makes sense; Development Workflow second, so yarn dev is actually running on your laptop; Catalog third, so there's real entity data on the screen; and Customizing — the biggest domain — last, because editing EntityPage.tsx only means something once there's something on that page worth changing. Two days of integration lab and a readiness gate close it out, before practice questions and a mock exam.

☺ Explain it like I'm 10

Imagine you're building a treehouse and you have fourteen days. You could just start nailing on the fanciest bit — the crooked little sign that says "NO GIZMOS ALLOWED" — on day one, because it's the part you're most excited about. But there's no floor yet, so the sign has nowhere to hang. The smart order is: build the floor first, then the walls, then the roof, and then the sign, because now it actually attaches to something. This plan does the same thing with a Backstage exam. Customizing the portal's screens is the biggest, most exciting chunk of the test — but it goes last, once the floor (how the pieces talk to each other), the walls (getting it running on your machine) and the roof (the index of everything it knows about) are already standing.

🦋🦫Your hosts for this topic: Mira the Butterfly & Benny the Beaver — Mira owns Customizing, the domain this plan deliberately saves for last, and Benny makes sure every day before it ends with something that actually ran on a real screen.

The arithmetic — funding days by weight, not by domain count

☺ Like you're 10: Fourteen days, four sections, and the split isn't one-quarter each — it's however big each section's real slice of the test actually is.

Pick a total number of study hours for the four domains — reading the blueprint page, running commands from memory, working the drills — kept separate from the two-day integration lab and the mock-exam days, which get their own budget below. Then apply one rule: no domain gets extra time because it's the one you already know, and none gets shortchanged because it sounds like operations busywork. The CNCF's curriculum already decided how much each domain is worth; your only real decision is the total.

hours for a domain = total domain-study hours × that domain's weight.

This plan uses 20 hours of domain study across eleven days — a little under two hours a day, realistic for a 90-minute multiple-choice exam that doesn't need eleven weeks of devotion. Applying the four weights instead of splitting evenly produces this:

DomainWeightBy weight (20h)If split evenlyDifference
🦋 Customizing Backstage32%6.4h5.0h+1.4h
🦫 Backstage Development Workflow24%4.8h5.0h−0.2h
🦉 Backstage Infrastructure22%4.4h5.0h−0.6h
🐿️ Backstage Catalog22%4.4h5.0h−0.6h
Total100%20h20h0

Say the headline fact plainly, because it's the one candidates get backwards most often: Customizing Backstage and Development Workflow, at 32% and 24%, add up to 56% of the paper. That's a majority built from React, TypeScript, NPM/Yarn and Docker — skills most platform engineers arrive without, and the two domains this plan protects the hardest even though its day order puts them second and fourth, not first.

Study hours per domain — by weight (solid) vs. even split (pale) 🦋 Customizing Backstage · 32% 6.4h 🦫 Development Workflow · 24% 4.8h 🦉 Infrastructure · 22% 4.4h 🐿️ Catalog · 22% 4.4h even split = 5.0h Customizing (6.4h) plus Development Workflow (4.8h) is 11.2h of the 20 — 56%, the same share those two domains hold on the real exam
◆ Key idea

Weight decides how much time each domain gets. It does not decide when you study it. This plan spends the most hours on Customizing Backstage, but schedules those hours last — after Infrastructure has taught you what runs where, Development Workflow has a real app running on your laptop, and Catalog has put an actual entity on the screen. Edit EntityPage.tsx on Day 1 with no app running and no entity to render, and you're memorizing syntax in a vacuum instead of watching a real tab appear.

The two-week calendar at a glance

Every section below is just a calendar wrapped around one row of this table. Read the matching part of the blueprint page first each day, then do the hands-on task before you check your work against it — that order matters more than the reading itself.

DaysDomainWeightHours (of 20)What you build
1–2🦉 Infrastructure22%4.4hThe client-server split drawn from memory, plus a production-shaped app-config.yaml
3–5🦫 Development Workflow24%4.8hA real app: create-app, yarn dev, a broken tsc fixed, a container image built
6–7🐿️ Catalog22%4.4hA catalog-info.yaml registered three ways, plus a deliberately broken one debugged
8–11🦋 Customizing32%6.4hA new EntityLayout.Route tab and a backend plugin wired into index.ts
12–13All fourThe integration lab: one portal, real data, a shipped image
14ReadinessThe gate, practice questions, then book the exam

Before Day 1 — pick your pace

☺ Like you're 10: Fourteen days is a guess about a specific kind of person. If that's not quite you yet, the plan stays the same shape — the number of days just changes.

"Fourteen days" assumes you're comfortable in a JavaScript/TypeScript toolchain already — you don't need production Backstage experience, but npm, yarn and a component-based frontend framework shouldn't be new vocabulary, because 56% of this exam lives there. If Kubernetes fundamentals are shaky too, this course's own Kubernetes Baseline You Need is worth a detour first, though CBA itself never mentions kubectl.

Your starting pointLaneWhat to change
Comfortable with React/TypeScript and a Node toolchain; have run at least one Yarn or NPM monorepo beforeStandard — 14 daysRun the plan exactly as written.
Solid backend or ops background, but React and a frontend build toolchain are genuinely newExtended — 20–21 daysMultiply every hours figure by 1.5, and add two extra days inside Customizing specifically — that domain is where an operations background shows fastest.
Already maintain a Backstage instance at work, or write React dailyCompressed — 8–9 daysMultiply every hours figure by 0.6, and skip straight to the "what surprises people" callouts inside each domain section instead of a full first read.
⚠ A knowledge exam hides a hands-on gap easily

CBA is entirely closed-book and multiple-choice — no terminal, no live portal — which means it's possible to recognize yarn workspace backend add on a page without ever having typed it. That's a real risk here specifically: the commands you run from memory below and the two-day lab aren't optional colour, they're what turns "I've read that the image builds from packages/backend/Dockerfile" into a fact that survives a scenario question phrased in a way you haven't seen written down before.

Days 1–2 — Backstage Infrastructure, the 22% that everything else assumes

☺ Like you're 10: Two days learning which half of the front desk talks to strangers and which half keeps the secrets — because you can't safely touch anything until you know that.

Read the Infrastructure section of the blueprint page on Day 1 and get one distinction so automatic you could say it half-asleep: the frontend is a React single-page app that holds no secrets and talks to the backend over HTTP; the backend is a Node service that owns the database, the GitHub credentials and every plugin that reaches a third-party system. Between them sits the catalog, backed by PostgreSQL in production — SQLite is development-only and loses everything on restart. On Day 2, write a production-shaped app-config.yaml from memory: a backend.database block pointed at Postgres with environment-variable substitution, a GitHub integration using a GitHub App rather than a personal token, and a catalog.locations entry.

Client-server split — who talks to whom, who holds what Frontend — packages/app React single-page app runs in the browser holds NO secrets Backend — packages/backend Node service GitHub creds · cluster tokens holds the real secrets HTTP API only PostgreSQL catalog + entities (SQLite = dev only)
🦆 Dot's-eye view

"The question that finally made this stick for me: 'where would I put a GitHub App private key?' If the honest answer is anywhere under packages/app, it ships to every browser that loads the portal. It only belongs in the backend, injected from the environment. Once I could answer that in one sentence, half the Infrastructure domain stopped being abstract.

Days 3–5 — Backstage Development Workflow, the 24%

☺ Like you're 10: Three days actually building the thing, not reading about it — because "understand local development workflows" is a competency you can only fake for so long.

Read the Development Workflow section of the blueprint page on Day 3, then run every command instead of just reading it. On Day 4, deliberately introduce a TypeScript error and watch yarn tsc catch it before you fix it — the exam expects you to know that type-checking is a real gate, not a formality. On Day 5, add one plugin's frontend and backend halves separately, then build a container image from the correct Dockerfile.

# Day 3 — bring a real app up, this is a monorepo you now OWN, not an install
npx @backstage/create-app@latest --path cba-study-lab
cd cba-study-lab
yarn install
yarn dev                       # app on :3000, backend on :7007

# Day 4 — break a type on purpose, watch the gate catch it, then fix it
yarn tsc                       # should be clean before you touch anything
#   ...edit a .tsx file, remove a required prop...
yarn tsc                       # now it isn't — read the error, then fix it

# Day 5 — a plugin ships in two halves; install BOTH
yarn workspace app add @backstage/plugin-kubernetes
yarn workspace backend add @backstage/plugin-kubernetes-backend

# Type-check, lint and test the whole workspace
yarn tsc && yarn lint:all && yarn test:all

# Production build, then a container image from the BACKEND Dockerfile —
# the compiled frontend is served BY the backend, so this is one image, not two
yarn build:all
yarn build:backend --config ../../app-config.yaml
docker image build . -f packages/backend/Dockerfile --tag cba-lab/backstage:study-plan
🦫 Benny's drill · 20 min

Close every tab. On paper or in a blank editor: name the two packages a fresh create-app produces and what runs on each of their default ports, name which file the production Dockerfile lives under and why the compiled frontend rides inside it, then write the two commands that install a plugin's frontend and backend halves separately. If you get through all three without looking anything up, Day 5 can shrink; if you can't, spend the extra time here rather than borrowing it from Customizing later — this domain is nearly a quarter of the paper on its own.

Days 6–7 — Backstage Catalog, the 22%

☺ Like you're 10: Two days keeping the big index honest — because the exam cares less about reading the index than about fixing it when something's wrong with it.

Read the Catalog section of the blueprint page on Day 6, then write a catalog-info.yaml from memory: a Component with a techdocs-ref annotation, an owner and a system. Get the three ingestion routes straight — manual registration (a human pastes a URL, creating a Location), static locations (URLs under catalog.locations in config) and automated ingestion (entity providers that crawl an organisation on a schedule and remove entities when the file disappears) — because only the last one scales, and the curriculum names manual and automated as separate competencies. On Day 7, break your own catalog-info.yaml on purpose — delete the required kind field — and read the processing error on the entity page, then fix it and query the same entity back out over the REST API.

# Day 6 target: write this from memory, then diff against cba-blueprint.html
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: study-lab-service
  description: The service this study plan's catalog entity describes.
  annotations:
    backstage.io/techdocs-ref: dir:.
spec:
  type: service
  lifecycle: experimental
  owner: group:default/platform-team
  system: cba-study-lab
# Day 7 — after you've broken and fixed catalog-info.yaml, confirm it landed
curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/catalog/entities/by-name/component/default/study-lab-service" \
  | jq '.metadata.annotations, .spec.owner'

# ghost entity? delete the LOCATION that created it, never the entity itself
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/catalog/locations" | jq .

Days 8–11 — Customizing Backstage, the 32%, last on purpose

☺ Like you're 10: Four days on the biggest section — the most study hours of the whole plan — saved for the end so there's finally something real on the screen to change.

This is the domain that catches operations people out, and the reason it comes last: frontend versus backend plugins only clicks once you've built both halves in Days 3–5, and editing EntityPage.tsx only feels concrete once there's a real entity from Days 6–7 to render it against. On Day 8, reread the Customizing section and fix the frontend/backend line for good: a frontend plugin is React, lives in packages/app, and must never hold a secret; a backend plugin is Node, lives in packages/backend, and is exactly where secrets belong. On Days 9–10, add a new tab to EntityPage.tsx using a Material UI Grid, wired against the entity you registered in Days 6–7. On Day 11, wire the matching backend plugin into the new-backend-system entry point.

// Day 9–10 target — packages/app/src/components/catalog/EntityPage.tsx
import { Grid } from '@material-ui/core';
import { EntityLayout, EntityAboutCard } from '@backstage/plugin-catalog';

const serviceEntityPage = (
  <EntityLayout>
    <EntityLayout.Route path="/" title="Overview">
      <Grid container spacing={3} alignItems="stretch">
        <Grid item md={6}><EntityAboutCard variant="gridItem" /></Grid>
      </Grid>
    </EntityLayout.Route>

    {/* new tab — this IS "customizing" in one edit, and it hot-reloads */}
    <EntityLayout.Route path="/study-notes" title="Study Notes">
      <Grid container spacing={3}>
        <Grid item md={12}><EntityAboutCard variant="gridItem" /></Grid>
      </Grid>
    </EntityLayout.Route>
  </EntityLayout>
);
// Day 11 target — packages/backend/src/index.ts, the OTHER half of the tab
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'));
backend.add(import('@backstage/plugin-techdocs-backend'));
backend.start();
⚠ "I installed the plugin and the tab is empty"

Almost always means you installed one half. A frontend plugin with no backend counterpart renders a card with nothing in it; a backend plugin with no frontend counterpart has nowhere to show up at all. When Day 11's tab stays blank, check packages/backend/src/index.ts before you touch a single line of React again.

Days 12–13 — the integration lab: one real portal, all four domains at once

☺ Like you're 10: Two days proving all four pieces actually fit together on one real front desk, instead of four separate practice projects that never met each other.

This is where eleven days of domain-by-domain study become one coherent thing that runs. Take the same cba-study-lab app from Days 3–5, the catalog-info.yaml from Days 6–7 and the EntityPage.tsx tab from Days 9–11, and carry all three into a single production-shaped pass: real Postgres instead of SQLite, an automated GitHub provider instead of the manual registration you used to learn the mechanics, and a shipped container image.

# Day 12 — move off SQLite, register your service via an automated provider
docker run -d --name cba-pg -e POSTGRES_PASSWORD=devpass -p 5432:5432 postgres:16

# app-config.production.yaml points backend.database.client at pg (Day 1–2 work)
# catalog.providers.github now crawls the org instead of one pasted URL (Day 6–7 work)
yarn build:backend --config ../../app-config.yaml --config ../../app-config.production.yaml

# Day 13 — ship it, then load the tab you built in Days 9–11 against the
# entity now sitting in a real database instead of a local SQLite file
docker image build . -f packages/backend/Dockerfile --tag cba-lab/backstage:integration
docker run -p 7007:7007 --env-file .env.production cba-lab/backstage:integration

If the "Study Notes" tab from Days 9–11 comes up blank against real data, or the provider never picks up your entity, that's the point — it's cheaper to hit both failure modes here than to only recognize their descriptions on exam day. Drill — Debug a Missing Catalog Entity walks the most common shape of that failure step by step if it fights you. Narrate which competency each command demonstrates out loud as you go — it's the fastest way to notice a domain you've been reading past without truly understanding.

Day 14 — the readiness gate, then book

☺ Like you're 10: Before anyone lets you sit a real test, a handful of honest checks — one per domain, plus one for the whole portal.

Don't open Mock Exam Set 1 until every box below is genuinely true, not "true if I squint." Recognizing a correct answer next to three wrong ones is a lower bar than producing the same fact cold — this checklist is written to catch the difference while it's still cheap to close.

Five or more checked, spanning at least three categories, is a reasonable green light. Fewer than that, don't force the calendar — go back to the specific day range above, not a general re-read of everything.

Once the gate is clear, work the CBA practice questions, then sit Mock Exam Set 1 and Set 2 a day or two apart rather than back to back. After each, log every miss and re-read only the domain it came from.

⚠ Verify officially before you book

This page doesn't repeat CBA's price, exact question count or every logistics detail — the blueprint page's logistics table has the full breakdown, separated deliberately into what the official pages state and what they don't publish. Whatever figure you land on there, confirm it again on the current Linux Foundation and CNCF pages before you pay: prices, windows and even domain weights are revised without announcement, and this is an independent, unofficial study resource, not affiliated with the CNCF or the Linux Foundation.

🎬 At Mission Control
🦊

Foxy: Fourteen days, four sections — I'll start with Customizing, obviously. It's the biggest, it's the exciting one, React tabs, all that.

🦋

Mira: It's mine, Foxy, and I'm still telling you: not Day 1. What are you going to put on that new tab, on Day 1, with no app running and nothing in the catalog to show?

🦊

Foxy: …a blank card. I'd be customizing a blank card.

🦫

Benny: Which is why Days 1 through 7 exist first. Get the app running, get one real entity registered — then your tab on Day 9 has something worth showing.

🐿️

Nutty: And I've counted — Customizing plus Development Workflow is fifty-six percent of the whole paper! Fifty-six! That's more than half, and neither of them is "read the docs," they're both "actually run the toolchain."

👺

Gizmo: Or — hot tip — skip straight to Day 9, screenshot someone else's EntityPage.tsx, done by lunch. 😈

🐢

Timmy: That's exactly what the Day 14 gate exists to catch. You don't open Set 1 on my watch until the boxes are honestly checked, Gizmo.

🦋

Mira: Build the floor before you hang the sign, Foxy. It's a better treehouse and it's a better exam score.

🐢 Timmy's checkpoint

1. Write the formula this plan uses to turn a domain's weight into study hours, and say what total it applies it to here. 2. Which two domains together make up 56% of the exam, and what does that number tell you about how to spend your time? 3. Why does this plan study Customizing Backstage — the biggest domain — last instead of first? 4. Name the three ways an entity enters the Backstage catalog, and say which one scales. 5. What's the difference between a frontend and a backend plugin, and which one may hold a GitHub App credential? 6. What happens on Days 12–13 that reading alone can't give you? 7. Name two of the seven items on the Day 14 readiness gate.

Check your answers
  1. hours for a domain = total domain-study hours × that domain's weight. This page applies it to a 20-hour domain-study budget spread across Days 1–11.
  2. Customizing Backstage (32%) and Backstage Development Workflow (24%), together 56%. It means more than half of this "portal" exam is actually a software-development exam — React, TypeScript, NPM/Yarn and Docker — and a study plan built only around catalog concepts is preparing for the smaller half of the paper.
  3. Because editing EntityPage.tsx or adding a plugin only makes sense once there's a running app (from Development Workflow) and a real entity in the catalog (from Catalog) to build against. Weight decides how many hours a domain gets; it doesn't decide when those hours happen, and Customizing has a hard dependency on the domains that come before it.
  4. Manual registration (a human registers a URL, creating a Location), static locations (URLs under catalog.locations in app-config.yaml), and automated ingestion via entity providers that crawl an organisation on a schedule. Only providers scale — and they also remove entities when the underlying file disappears.
  5. A frontend plugin is React, installed into packages/app, and runs in the browser — it must never hold a secret. A backend plugin is Node, installed into packages/backend, and is exactly where credentials like a GitHub App belong.
  6. Days 12–13 carry the app, the catalog entity and the customized tab from earlier in the week into one production-shaped pass — real Postgres instead of SQLite, an automated provider instead of manual registration, a shipped container image — so you watch the whole thing behave together rather than only recognizing four domains described separately on a page.
  7. Any two of: explaining frontend vs. backend plugins and which may hold a secret; having added a real tab and watched it hot-reload against real data; naming the two packages create-app produces and the correct production Dockerfile; having broken a type on purpose and watched tsc catch it; drawing the client-server split from memory; naming all three catalog ingestion routes and which scales; having run all four domains together in the Days 12–13 lab.