Backstage Infrastructure
The Backstage Infrastructure domain tests how a Backstage app is configured and deployed: config file layering, environment and secret handling, backend runtime settings, database choice, authentication, ingress, and shared storage for TechDocs.
Configuration layers
Backstage reads app-config.yaml by default and layers additional files passed with --config, such as app-config.production.yaml. app-config.local.yaml is for local overrides and is normally gitignored. Values can be overridden by environment variables prefixed APP_CONFIG_, and app-config.yaml can reference environment variables with ${VAR} substitution. Keep secrets out of committed YAML: inject them through environment variables or a secret manager. Production must set public app.baseUrl and backend.baseUrl correctly; these URLs drive links, CORS, cookies, and OAuth redirects.
Runtime and data
In production, build the frontend and backend, then run the backend; the app backend plugin serves the built frontend bundle, or you can serve static assets separately. Set backend.listen to a host and port reachable from the container network, commonly 0.0.0.0:7007. Do not use SQLite in production: use PostgreSQL via backend.database.client: pg and a connection string from a secret. Configure auth under auth.providers (for example GitHub or Okta) and service-to-service auth keys for backend integrations. TechDocs needs a builder and publisher; with multiple replicas, use a shared publisher such as external object storage rather than local disk. Backends should be stateless behind an ingress; use a shared database and shared TechDocs storage.
Worked example
A Kubernetes deployment fails after two replicas. The config uses SQLite at :memory: and a local TechDocs publisher. Fix: set backend.database.client: pg with connection: ${POSTGRES_URL} from a Secret; set techdocs.publisher.type: googleGcs (or another shared publisher) with credentials from a Secret; set backend.listen: 0.0.0.0:7007; set app.baseUrl and backend.baseUrl to the public HTTPS ingress. Reasoning: each replica had its own in-memory catalog and TechDocs files, so requests hit inconsistent state. Shared PostgreSQL and object storage make replicas interchangeable; correct base URLs keep OAuth and API calls on the ingress origin.
Traps
Do not leave backend.baseUrl as http://localhost:7007 in production. Do not commit app-config.local.yaml or secrets. Do not assume the frontend and backend share an origin unless ingress routes both. Do not scale TechDocs with a local publisher; the generated files disappear or diverge across pods. Do not forget that APP_CONFIG_ overrides and ${VAR} substitution are different mechanisms.