Command Palette

Search for a command to run...

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.

50 min Free (S3 for docs storage in production) 5 steps 1 break-it drills

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

shoplite-platform/
  • 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. 1

    Add docs to a service

    backstage.io/techdocs-ref: dir:. in catalog-info.yaml (already set in Mission 1.1) points TechDocs at this mkdocs.yml.

    checkout-api/mkdocs.ymlwhole fileyaml
    site_name: checkout-api
    nav:
      - Overview: index.md
      - Runbook: runbook.md
      - Architecture decisions: adr/
    plugins: [techdocs-core]
  2. 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. 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. 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:backend plus the provided Dockerfile produces the image.

    terminal
    $ yarn install --immutable && yarn tsc && yarn build:backend
    docker 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. 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.

terminal
$ kubectl -n checkout get pods --show-labels | head -2
── what you'll see ──
NAME READY STATUS LABELS
checkout-6d8f9c7b5-x4k2p 1/1 Running app.kubernetes.io/name=checkout,pod-template-hash=6d8f9c7b5

Part 5

Interview questions from this mission

01

How would you get developers to actually use an internal portal?

02

What are scorecards in a developer portal?

0/3 · 0%