Mission 1.3 · Stage 1 — The Developer Portal: Backstage and Golden Paths
TechDocs, Plugins, and Scorecards
Goal: Each service page in Backstage shows its docs, pods, Argo CD deployments, and a production-readiness score, so developers and on-call engineers have one place to look.
By the end of this mission
- Publish docs-as-code with TechDocs
- Add the Kubernetes and Argo CD plugins to entity pages
- Define production-readiness checks and show them as scorecards
- Deploy Backstage itself properly (auth, Postgres, GitOps)
Part 1
Understand it first
Docs as code
TECHDOCS renders Markdown from each repo (MkDocs under the hood) into the service's page. Docs live next to the code and change in the same PRs, so they stay current. In production, CI generates the static site and publishes it to S3 (techdocs-cli publish), and Backstage serves it from there. Runbooks for on-call belong here too, linked from alerts.
Plugins bring context
The KUBERNETES plugin shows pods, restarts, and errors across clusters for the entity's label selector. The ARGO CD plugin (community) shows sync and health status and history. Others: GitHub Actions runs, PagerDuty on-call and incidents, Grafana dashboards, SonarQube, security scanners, and cost (OpenCost). The goal isn't every plugin; it's the few that answer the questions developers and on-call engineers actually ask ('is my deploy out? is it healthy? who's on call?').
Scorecards
A SCORECARD measures each service against standards: has an owner, has a runbook link, has an SLO, has a PDB, CI uses the current reusable workflow, no critical vulnerabilities, on a supported Java version. It turns platform standards into visible, team-level progress (gamification works) instead of mandates. Options: Backstage Tech Insights (community), or commercial portals with built-in scorecards. Kyverno PolicyReports (Mission 0.4) can feed it.
Part 2
Your project after this mission · 6 files change
- checkout-api/
- docs/
- index.mdnew
- runbook.mdnew
- mkdocs.ymlnew
- shoplite-gitops/
- apps/
- backstage/
- base/
- new
- shoplite-portal/
- packages/
- app/
- src/
- components/
- catalog/
- EntityPage.tsxmodified
- app-config.production.yamlnew
Part 3
Build it, step by step
- 1
Add docs to a service
backstage.io/techdocs-ref: dir:.in catalog-info.yaml (already set in Mission 1.1) points TechDocs at thismkdocs.yml.checkout-api/mkdocs.ymlwhole fileyaml site_name: checkout-api nav: - Overview: index.md - Runbook: runbook.md - Architecture decisions: adr/ plugins: [techdocs-core] - 2
Kubernetes and Argo CD on the entity page
Install the plugins, configure cluster access (a read-only ServiceAccount per cluster, or IRSA for EKS), and add the tabs to the service page layout.
shoplite-portal/packages/app/src/components/catalog/EntityPage.tsxadd to filetsx import { EntityKubernetesContent } from '@backstage/plugin-kubernetes'; import { EntityArgoCDOverviewCard, isArgocdAvailable } from '@roadiehq/backstage-plugin-argo-cd'; // in serviceEntityPage: <EntityLayout.Route path="/kubernetes" title="Kubernetes"> <EntityKubernetesContent refreshIntervalMs={30000} /> </EntityLayout.Route> <EntityLayout.Route path="/deployments" title="Deployments" if={isArgocdAvailable}> <EntityArgoCDOverviewCard /> </EntityLayout.Route> - 3
Production configuration
Real auth (GitHub or OIDC SSO, never the guest provider), PostgreSQL for the catalog and scaffolder, TechDocs from S3, and secrets from environment variables populated by External Secrets (GitOps course, Mission 2.1).
shoplite-portal/app-config.production.yamlwhole fileyaml app: { baseUrl: https://portal.shoplite.dev } backend: baseUrl: https://portal.shoplite.dev database: client: pg connection: host: ${POSTGRES_HOST} user: ${POSTGRES_USER} password: ${POSTGRES_PASSWORD} auth: environment: production providers: github: production: clientId: ${AUTH_GITHUB_CLIENT_ID} clientSecret: ${AUTH_GITHUB_CLIENT_SECRET} signIn: { resolvers: [{ resolver: usernameMatchingUserEntityName }] } techdocs: builder: external publisher: type: awsS3 awsS3: { bucketName: shoplite-techdocs, region: ap-south-1 } kubernetes: serviceLocatorMethod: { type: multiTenant } clusterLocatorMethods: - type: config clusters: - { name: prod-mumbai, url: https://ABC.gr7.ap-south-1.eks.amazonaws.com, authProvider: aws } - 4
Build and deploy Backstage with the golden path
The portal is itself a service: a container image built in CI and deployed with the same GitOps flow as everything else, which is good dogfooding.
yarn build:backendplus the provided Dockerfile produces the image.terminal$ yarn install --immutable && yarn tsc && yarn build:backenddocker build . -f packages/backend/Dockerfile -t 123456789012.dkr.ecr.ap-south-1.amazonaws.com/backstage:$(git rev-parse --short HEAD)argocd app get backstage-prod-mumbai | grep -E 'Sync|Health'── expected output ──Sync Status: Synced to main (5e6f7a8)Health Status: Healthy - 5
Define production-readiness checks
Start with a short list everyone agrees on, and show it per service. This YAML describes the checks; with Tech Insights, each check is a fact retriever plus a rule (for example 'catalog-info has a runbook link', 'repo has CODEOWNERS', 'no critical CVEs in the latest image scan').
shoplite-portal/scorecards/production-readiness.yamlwhole fileyaml scorecard: production-readiness checks: - { id: has-owner, description: Owned by an existing team } - { id: has-runbook, description: Runbook link in catalog-info } - { id: has-slo, description: SLO defined in Sloth/Pyrra config } - { id: pdb-and-probes, description: PDB and probes present (from Kyverno PolicyReport) } - { id: current-ci, description: Uses platform-workflows java-service.yml@v4 or newer } - { id: no-critical-cves, description: No critical CVEs in the deployed image } levels: { bronze: 3, silver: 5, gold: 6 }
Checkpoint — you should now have
- ✓Service pages show docs, pods across clusters, and Argo CD deploy history.
- ✓Backstage runs in the cluster with SSO and PostgreSQL, deployed via GitOps.
- ✓Each service shows its production-readiness level.
Part 4
Break it on purpose
Make each change, run the command, and read the error before revealing the diagnosis. Recognising these messages on sight is what makes you fast on a real team. Undo the change afterwards.
Break #1
Kubernetes tab is empty
The checkout entity page's Kubernetes tab shows 'No resources' although pods are running.
Part 5
Interview questions from this mission
How would you get developers to actually use an internal portal?
What are scorecards in a developer portal?