Skip to main content
Version: v1.2.0-m.1 (pre-release)

Backstage Configuration

OpenChoreo uses Backstage as its developer portal, providing a unified interface for managing organizations, projects, components, and deployments. This guide covers configuration options for customizing Backstage in your OpenChoreo deployment.

Backstage configuration is done via Helm values in the Control Plane chart.

Core Configuration

Console/UI URL:

backstage:
baseUrl: "https://console.example.com"
http:
hostnames:
- console.example.com

backstage.baseUrl sets the URL used for OAuth redirects and internal link generation. backstage.http.hostnames configures the Gateway API HTTPRoute that routes traffic to Backstage.

OpenChoreo API URL:

openchoreoApi:
http:
hostnames:
- api.example.com
config:
server:
publicUrl: "https://api.example.com"

Authentication and Authorization

By default, OpenChoreo configures ThunderID as the identity provider for Backstage with a pre-configured OAuth client for testing purposes. If you want to integrate an external identity provider instead, follow the steps below to configure both authentication and authorization for the new client.

Authentication

Backstage uses two separate OAuth 2.0 clients:

ClientGrant typePurpose
Sign-in clientauthorization_codeUser login via browser
Service clientclient_credentialsBackground tasks — catalog sync, API calls

You can register them as a single OAuth client that supports both grant types, or as two distinct clients. Using two distinct clients is recommended for production as it lets you apply least-privilege scopes to each and rotate them independently.

OAuth Client Requirements:

For the sign-in client:

  1. Grant Types: authorization_code
  2. Token Format: JWT tokens (not opaque tokens)
  3. Redirect URLs: Add the Backstage callback URL:
    • <protocol>://<backstage-domain>/api/auth/openchoreo-auth/handler/frame
  4. User Claims: Configure the access token to include:
    • family_name, given_name, email, groups

For the service client:

  1. Grant Types: client_credentials
  2. Token Format: JWT tokens

Helm Configuration:

Add both client secrets to the Backstage credentials Secret. If you are using a single shared client, set both client-secret and service-client-secret to the same value:

kubectl create secret generic backstage-secrets \
-n openchoreo-control-plane \
--from-literal=backend-secret="your-32-character-secret-here" \
--from-literal=client-secret="your-sign-in-client-secret" \
--from-literal=service-client-secret="your-service-client-secret" \
--from-literal=jenkins-api-key="not-used" \
--dry-run=client -o yaml | kubectl apply -f -
backstage:
secretName: "backstage-secrets"
auth:
# Sign-in client (authorization_code flow)
clientId: "your-sign-in-client-id"
redirectUrls:
- "<protocol>://<backstage-domain>/api/auth/openchoreo-auth/handler/frame"
oidcScope: "openid profile email"
# Service client (client_credentials flow — background tasks)
serviceClientId: "your-service-client-id"
serviceClientSecretKey: "service-client-secret"
scope: ""
note

If serviceClientId is not set, it falls back to clientId. If serviceClientSecretKey is not set, it falls back to client-secret. This means existing single-client deployments require no changes.

Scope Configuration:

FieldDescriptionDefault
backstage.auth.oidcScopeSpace-separated scopes requested during user login via the authorization_code flow (sign-in client)"openid profile email"
backstage.auth.scopeSpace-separated scopes requested when the service client fetches tokens via client_credentials. Leave empty to use the IdP default, or set explicitly when required by your IdP (e.g. "api://client-id/.default" for Azure AD).""

See Identity Provider Configuration for detailed setup instructions.

Authorization

With authorization enabled by default, Backstage uses the client_credentials grant (via the service client) to authenticate with the OpenChoreo API as a service account. The API matches the sub claim in the issued JWT to identify the caller, so the service client must be granted the backstage-catalog-reader role via a bootstrap authorization mapping.

Add the following to your values override file, replacing your-service-client-id with the value of backstage.auth.serviceClientId (or backstage.auth.clientId if you are using a single shared client):

openchoreoApi:
config:
security:
authorization:
bootstrap:
mappings:
- name: backstage-catalog-reader-binding
kind: ClusterAuthzRoleBinding
system: true
roleMappings:
- roleRef:
name: backstage-catalog-reader
kind: ClusterAuthzRole
entitlement:
claim: sub
value: "your-service-client-id"
effect: allow

Feature Flags

backstage:
features:
workflows:
enabled: true # Requires Workflow Plane
observability:
enabled: true # Requires Observability Plane
auth:
redirectFlow:
enabled: true # Silently redirect to IDP instead of showing sign-in popup
assistant:
enabled: false # "Ask Perch" chat drawer; requires perch-agent deployment
perchAgentUrl: "" # Defaults to in-cluster perch-agent Service

# Top-level (not under backstage.features) — toggles both the API server and Backstage UI
features:
secretManagement:
enabled: false # Enable the Secret management API and Backstage UI
FlagDescriptionDefault
backstage.features.workflows.enabledShow Workflows tab and overview card. Requires Workflow Plane.true
backstage.features.observability.enabledShow Metrics, Traces, Runtime Logs tabs and runtime health card. Requires Observability Plane.true
backstage.features.auth.redirectFlow.enabledSilently redirect to the IdP instead of showing the Backstage sign-in popup.true
backstage.features.assistant.enabledEnable the "Ask Perch" chat affordance. Requires the perch-agent service to be deployed.false
backstage.features.assistant.perchAgentUrlUpstream URL for the perch-agent. Leave empty to default to the in-cluster Service in the release namespace.""
features.secretManagement.enabledEnable the Secret management API on the API server and the corresponding UI in Backstage. Top-level — not under backstage.features.false

Catalog Sync

OpenChoreo entities are synced to the Backstage catalog through two mechanisms:

  • Event-driven sync: A control-plane component watches OpenChoreo Kubernetes resources and posts change notifications to Backstage. Backstage applies a delta update to the catalog within seconds.
  • Periodic full sync: A scheduled task in Backstage re-fetches all OpenChoreo state and applies a full update. This catches anything missed by the event path.

Both are enabled by default. If the event-driven sync is disabled, the periodic full sync becomes the only sync mechanism.

backstage:
catalogSync:
frequency: 300 # Periodic full sync interval in seconds

# Top-level — controls both the event-forwarder deployment and Backstage's
# event subscription. When false, Backstage runs in poll-only mode.
eventForwarder:
enabled: true
ParameterDescriptionDefault
backstage.catalogSync.frequencySeconds between periodic full syncs. Lower the value when eventForwarder.enabled is false.300
eventForwarder.enabledEnables the event-forwarder component and the Backstage event subscription (via OPENCHOREO_EVENTS_ENABLED). Top-level, not under backstage.true

The Backstage container's OPENCHOREO_EVENTS_ENABLED env var is wired directly from eventForwarder.enabled — there is no separate backstage.events.enabled chart value. See the Helm chart reference for full eventForwarder.* configuration.

Resource Configuration

backstage:
replicas: 1
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 200m
memory: 256Mi

autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 3

Environment Variables

The chart sets two groups of environment variables on the Backstage container:

Default Variables (backstage.env)

These are the base variables defined in the chart. They can be overridden in a values file, but avoid using --set backstage.env[0].name=... on the command line — Helm's sparse array handling can corrupt the list.

backstage:
env:
- name: NODE_ENV
value: production
- name: LOG_LEVEL
value: info
- name: PORT
value: "7007"

Extra Variables (backstage.extraEnv)

To add custom environment variables without touching the defaults, use backstage.extraEnv. It is appended after backstage.env in the container spec, so if you use the same key, the last value wins.

backstage:
extraEnv:
- name: CUSTOM_VAR
value: "my-value"
- name: LOG_LEVEL # overrides the default "info"
value: debug

All Environment Variables

The chart sets the following environment variables on the Backstage container. Variables marked "from Secret" are read from the Secret referenced by backstage.secretName.

VariableDescriptionSource
NODE_ENVNode.js environmentbackstage.env (default: production)
LOG_LEVELLogging verbositybackstage.env (default: info)
PORTHTTP server portbackstage.env (default: 7007)
BACKSTAGE_BASE_URLPublic URL for OAuth redirects and linksbackstage.baseUrl
OPENCHOREO_API_URLInternal OpenChoreo API endpointbackstage.openchoreoApi.url (auto-configured)
BACKEND_SECRETSession encryption keyfrom Secret (backend-secret)
OPENCHOREO_AUTH_CLIENT_IDSign-in client ID (authorization_code flow)backstage.auth.clientId
OPENCHOREO_AUTH_CLIENT_SECRETSign-in client secretfrom Secret (client-secret)
OPENCHOREO_SERVICE_CLIENT_IDService client ID (client_credentials flow — background tasks)backstage.auth.serviceClientId (falls back to clientId)
OPENCHOREO_SERVICE_CLIENT_SECRETService client secretfrom Secret (key backstage.auth.serviceClientSecretKey, default: client-secret)
OPENCHOREO_AUTH_METADATA_URLOIDC discovery URL (omitted when unset)security.oidc.wellKnownEndpoint
OPENCHOREO_AUTH_AUTHORIZATION_URLOAuth authorization endpointsecurity.oidc.authorizationUrl
OPENCHOREO_AUTH_TOKEN_URLOAuth token endpoint (shared by both clients)security.oidc.tokenUrl
OPENCHOREO_AUTH_OIDC_SCOPEOIDC scopes for the sign-in clientbackstage.auth.oidcScope (default: openid profile email)
OPENCHOREO_AUTH_SCOPEScopes for the service client's client_credentials token requestbackstage.auth.scope (default: "")
OPENCHOREO_FEATURES_AUTH_ENABLEDEnable authenticationsecurity.enabled
OPENCHOREO_FEATURES_AUTHZ_ENABLEDEnable authorizationsecurity.authz.enabled
OPENCHOREO_FEATURES_AUTH_REDIRECT_FLOW_ENABLEDSilent redirect vs sign-in popupbackstage.features.auth.redirectFlow.enabled
OPENCHOREO_FEATURES_WORKFLOWS_ENABLEDShow Workflows UIbackstage.features.workflows.enabled
OPENCHOREO_FEATURES_OBSERVABILITY_ENABLEDShow Metrics/Traces/Logs UIbackstage.features.observability.enabled
OPENCHOREO_FEATURES_ASSISTANT_ENABLEDEnable the "Ask Perch" chat affordancebackstage.features.assistant.enabled
OPENCHOREO_PERCH_AGENT_URLUpstream URL for the perch-agent (in-cluster fallback applied)backstage.features.assistant.perchAgentUrl
OPENCHOREO_FEATURES_SECRET_MANAGEMENT_ENABLEDEnable the Secret management UIfeatures.secretManagement.enabled (top-level)
OPENCHOREO_EVENTS_ENABLEDEnable event-driven catalog synceventForwarder.enabled (top-level)
OPENCHOREO_CATALOG_SYNC_FREQUENCYPeriodic full-sync interval (seconds)backstage.catalogSync.frequency
JENKINS_BASE_URLJenkins server base URL (consumed when Jenkins integration is enabled)backstage.externalCI.jenkins.baseUrl
JENKINS_USERNAMEJenkins username for API authenticationbackstage.externalCI.jenkins.username
JENKINS_API_KEYJenkins API keyfrom Secret (jenkins-api-key)
GITHUB_HOSTGitHub host for the Actions integration (github.com or a GHES host)backstage.externalCI.githubActions.host
GITHUB_API_BASE_URLGitHub API base URL (set for GHES, e.g. https://ghe.example.com/api/v3)backstage.externalCI.githubActions.apiBaseUrl
GITHUB_TOKENGitHub backend token (catalog, scaffolder, TechDocs — not the Actions card)from Secret (github-actions-token, optional)
AUTH_GITHUB_CLIENT_IDGitHub OAuth App client ID for per-user sign-in (powers the Actions card)backstage.externalCI.githubActions.oauth.clientId
AUTH_GITHUB_CLIENT_SECRETGitHub OAuth App client secretfrom Secret (github-oauth-client-secret, optional)
DATABASE_CLIENTDatabase driver (better-sqlite3 or pg)backstage.database.type
SQLITE_STORAGE_DIRSQLite database directory (when using SQLite)backstage.database.sqlite.mountPath
POSTGRES_HOST / _PORT / _USER / _PASSWORD / _DBPostgreSQL connection fields (when database.type=postgresql)from Secret (postgres-host, postgres-port, postgres-user, postgres-password, postgres-db)
PGSSLMODESet to require when PostgreSQL SSL is enabledbackstage.database.postgresql.ssl (conditional)

HTTP Routing Configuration

OpenChoreo uses the Gateway API for traffic routing (not Kubernetes Ingress). The chart creates HTTPRoute resources that route traffic through a shared Gateway managed by kgateway.

backstage:
http:
enabled: true
hostnames:
- console.example.com

TLS is configured at the gateway level, not per-HTTPRoute. See the gateway configuration section in the Helm chart values.

Service Configuration

backstage:
service:
type: ClusterIP
port: 7007

OpenChoreo API Integration

Backstage communicates with the OpenChoreo API for backend operations:

backstage:
openchoreoApi:
url: "" # Auto-configured to internal service URL

The API URL defaults to http://openchoreo-api.{namespace}.svc.cluster.local:8080/api/v1.

External CI Integration

OpenChoreo Backstage ships with two external CI integrations — Jenkins and GitHub Actions — both wired through backstage.externalCI.*. For the end-to-end walkthroughs (OAuth setup, component annotations, what you see in the portal), see External CI Integration. This section is the configuration reference.

Jenkins

The chart wires Jenkins integration into Backstage via backstage.externalCI.jenkins.*. The JENKINS_BASE_URL, JENKINS_USERNAME, and JENKINS_API_KEY env vars are always set on the container; the jenkins-api-key Secret key is required even when Jenkins is not in use (use a placeholder value).

backstage:
externalCI:
jenkins:
enabled: false # Toggle Jenkins integration on/off
baseUrl: "https://jenkins.example.com"
username: "jenkins-user"
ParameterDescriptionDefault
backstage.externalCI.jenkins.enabledToggle Jenkins integrationfalse
backstage.externalCI.jenkins.baseUrlJenkins server base URL"https://jenkins.example.com"
backstage.externalCI.jenkins.usernameJenkins username for API authentication"jenkins-user"

The Jenkins API token is read from the jenkins-api-key key in backstage.secretName.

GitHub Actions

The chart wires GitHub Actions integration into Backstage via backstage.externalCI.githubActions.*. Once enabled, the Component page surfaces a CI/CD tab showing recent workflow_run data for any Component annotated with github.com/project-slug.

backstage:
externalCI:
githubActions:
enabled: false # Toggle GitHub Actions integration on/off
host: "github.com" # GHES: set to your instance host, e.g. ghe.example.com
apiBaseUrl: "" # GHES: set to e.g. https://ghe.example.com/api/v3
oauth:
clientId: "" # GitHub OAuth App client ID (powers per-user sign-in)
ParameterDescriptionDefault
backstage.externalCI.githubActions.enabledToggle GitHub Actions integrationfalse
backstage.externalCI.githubActions.hostGitHub host (github.com or a GHES host)"github.com"
backstage.externalCI.githubActions.apiBaseUrlGitHub API base URL (required for GHES, e.g. https://ghe.example.com/api/v3)""
backstage.externalCI.githubActions.oauth.clientIdGitHub OAuth App client ID for the per-user sign-in the Actions card uses""
Two GitHub credentials are involved

The GitHub Actions card authenticates each portal user through a GitHub OAuth App (oauth.clientId plus the github-oauth-client-secret Secret key) — this is what lets a user see runs for repositories they can access. The optional github-actions-token Secret key is a separate backend token used by catalog ingestion, the scaffolder, and TechDocs — it is not what the Actions card reads. See the GitHub Actions walkthrough for the full setup.

Operator Extensions

The chart exposes several extension points so platform engineers can inject custom Backstage configuration without building a custom container image.

backstage:
# Arbitrary Backstage app-config YAML rendered into a ConfigMap and
# appended as `--config app-config.extra.yaml`. Preferred for operator
# overlays (auth providers, integrations, custom theme, etc.).
appConfig: {}

# Additional CLI args appended after the built-in --config flags.
extraArgs: []

# Extra volumes and mounts merged into the pod spec.
extraVolumes: []
extraVolumeMounts: []

# Chart-managed TLS toggle. When false (the default), the chart
# automatically mounts an `app-config.csp.yaml` to prevent
# upgrade-insecure-requests. Set to true when terminating TLS at the
# Backstage Service itself (rare — TLS is normally terminated at the
# Gateway).
tls:
enabled: false
warning

backstage.appConfig is rendered into a plaintext ConfigMap. Do not put secrets here — reference them through backstage.extraEnv with secretKeyRef instead.

Backstage Secret

Backstage credentials are loaded from a Kubernetes Secret referenced by backstage.secretName.

warning

backstage.secretName is required when backstage.enabled=true. The chart fails the install with a placeholder-hostname validation error if secretName is empty.

Required keys:

  • backend-secret: Backstage session encryption key
  • client-secret: Sign-in client secret (authorization_code flow)
  • jenkins-api-key: Jenkins integration API key (use a placeholder if not using Jenkins)

Optional keys:

  • service-client-secret: Service client secret (client_credentials flow). Required only when using a dedicated service client (backstage.auth.serviceClientSecretKey: "service-client-secret"). When not present, the service client falls back to client-secret.
  • github-oauth-client-secret: GitHub OAuth App client secret. Required only when the GitHub Actions card is enabled (backstage.externalCI.githubActions.oauth.clientId set) — see the GitHub Actions walkthrough.
  • github-actions-token: GitHub backend token for catalog ingestion, the scaffolder, and TechDocs. Declared optional: true on the Deployment, so the pod still starts without it. Not the credential the GitHub Actions card uses to read runs.

When backstage.database.type=postgresql, the same Secret must also include:

  • postgres-host
  • postgres-port
  • postgres-user
  • postgres-password
  • postgres-db
External Secrets Operator

If you are using External Secrets Operator (ESO) to manage backstage-secrets (as in the default k3d setup), do not use kubectl create secret directly — ESO will overwrite your changes on its next sync. Instead, add the PostgreSQL credentials to your secret store (e.g., OpenBao, AWS Secrets Manager) and update the ExternalSecret resource to include the additional keys.

backstage:
secretName: "backstage-secrets"

Database Configuration

Backstage requires a database to store catalog entities, user settings, and plugin data. OpenChoreo supports two database backends:

BackendUse CasePersistenceScalability
SQLite (default)Development, single-replicaOptional (emptyDir or PVC)Single replica only
PostgreSQLProduction, multi-replicaExternal databaseHorizontal scaling

Default Configuration (SQLite)

By default, Backstage uses SQLite with an emptyDir volume, which means data is lost when the pod restarts:

backstage:
database:
type: sqlite
sqlite:
persistence:
enabled: false # Uses emptyDir (data lost on restart)

SQLite with Persistence

For development environments where you want data to persist across restarts but don't need horizontal scaling:

backstage:
database:
type: sqlite
sqlite:
mountPath: /app/.config/backstage
persistence:
enabled: true
size: 1Gi
storageClassName: "" # Uses default storage class
accessMode: ReadWriteOnce
warning

SQLite locks the database file, preventing multiple Backstage replicas from running simultaneously. For high-availability deployments, use PostgreSQL.

PostgreSQL for Production

For production deployments requiring high availability and data durability, configure an external PostgreSQL database.

Prerequisites

  1. Provision a PostgreSQL Database: Use a managed service (AWS RDS, Google Cloud SQL, Azure Database for PostgreSQL) or a self-hosted PostgreSQL instance
  2. Create a Database: Create a database for Backstage (e.g., backstage)
  3. Create a Database User: Create a user with full access to the database

Backstage automatically creates per-plugin databases (e.g., backstage_plugin_catalog, backstage_plugin_auth) using the provided credentials.

Helm Configuration

For managed PostgreSQL services or external databases:

kubectl create secret generic backstage-secrets \
-n openchoreo-control-plane \
--from-literal=backend-secret="your-32-character-secret-here" \
--from-literal=client-secret="your-client-secret" \
--from-literal=jenkins-api-key="not-used" \
--from-literal=postgres-host="postgres.example.com" \
--from-literal=postgres-port="5432" \
--from-literal=postgres-user="backstage" \
--from-literal=postgres-password="your-secure-password" \
--from-literal=postgres-db="backstage" \
--dry-run=client -o yaml | kubectl apply -f - && \
helm upgrade --install openchoreo-control-plane oci://ghcr.io/openchoreo/helm-charts/openchoreo-control-plane \
--version 1.2.0-m.1 \
--namespace openchoreo-control-plane \
--reuse-values \
--set backstage.secretName="backstage-secrets" \
--set backstage.database.type=postgresql \
--set backstage.database.postgresql.ssl=true
tip

Set backstage.database.postgresql.ssl=true when connecting to managed PostgreSQL services (AWS RDS, Google Cloud SQL, Azure Database for PostgreSQL) that enforce SSL connections.

PostgreSQL Configuration Reference

ParameterDescriptionDefault
backstage.database.typeDatabase backend (sqlite or postgresql)sqlite
backstage.database.postgresql.sslEnable SSL for PostgreSQL connections (sets PGSSLMODE=require)false
backstage.secretNameSecret containing Backstage and PostgreSQL credentials""

PostgreSQL connection fields are read from Secret keys (postgres-host, postgres-port, postgres-user, postgres-password, postgres-db) referenced by backstage.secretName.

Verifying PostgreSQL Connection

After deploying with PostgreSQL, verify the connection:

# Check Backstage pod is running
kubectl get pods -n openchoreo-control-plane -l app.kubernetes.io/component=backstage

# Verify database environment variables are set to PostgreSQL
kubectl get deployment backstage -n openchoreo-control-plane \
-o jsonpath='{range .spec.template.spec.containers[0].env[*]}{.name}={.value}{"\n"}{end}' \
| grep -E "DATABASE|POSTGRES"

# Check logs for database connection errors
kubectl logs -n openchoreo-control-plane deployment/backstage | head -50

If PostgreSQL is configured correctly, Backstage will create per-plugin databases automatically:

# List databases created by Backstage (run against your PostgreSQL instance)
psql -U backstage -d backstage -c "\l" | grep backstage_plugin

Expected output shows databases like backstage_plugin_catalog, backstage_plugin_auth, etc.

Health Checks

Backstage health checks are pre-configured:

CheckEndpointDefault
Liveness/healthcheckEnabled
Readiness/healthcheckEnabled

Example Configurations

backstage:
enabled: true
replicas: 1
baseUrl: "http://openchoreo.localhost:8080"
http:
hostnames:
- openchoreo.localhost
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 100m
memory: 256Mi
features:
workflows:
enabled: true
observability:
enabled: true

Troubleshooting

Backstage Not Loading

Check pod status and logs:

kubectl get pods -n openchoreo-control-plane -l app.kubernetes.io/component=backstage
kubectl logs -n openchoreo-control-plane deployment/backstage

Authentication Issues

Verify OAuth configuration by checking the deployment spec (the Backstage image is distroless, so exec commands like env are not available):

# Check Backstage auth-related environment variables
kubectl get deployment backstage -n openchoreo-control-plane \
-o jsonpath='{range .spec.template.spec.containers[0].env[*]}{.name}={.value}{"\n"}{end}' \
| grep -E "AUTH|THUNDER"

# Check ThunderID is running
kubectl get pods -n thunder -l app.kubernetes.io/name=thunder

API Connection Errors

Verify OpenChoreo API is accessible:

# Check API pod
kubectl get pods -n openchoreo-control-plane -l app.kubernetes.io/component=api-server

# Check API logs for errors
kubectl logs -n openchoreo-control-plane deployment/openchoreo-api --tail=20

Database Connection Issues

If Backstage fails to start with database errors:

For SQLite errors:

# Check for SQLite-specific errors in logs
kubectl logs -n openchoreo-control-plane deployment/backstage | grep -i "sqlite\|database"

# Check the volume type (emptyDir vs PVC)
kubectl get deployment backstage -n openchoreo-control-plane \
-o jsonpath='{.spec.template.spec.volumes}' | python3 -m json.tool

For PostgreSQL errors:

# Verify PostgreSQL environment variables are configured
kubectl get deployment backstage -n openchoreo-control-plane \
-o jsonpath='{range .spec.template.spec.containers[0].env[*]}{.name}={.value}{"\n"}{end}' \
| grep -E "DATABASE|POSTGRES"

# Check for connection errors in logs
kubectl logs -n openchoreo-control-plane deployment/backstage | grep -i "postgres\|connection\|ECONNREFUSED"

# Verify the secret has all required postgres keys
kubectl get secret backstage-secrets -n openchoreo-control-plane \
-o jsonpath='{.data}' | python3 -c "import json,sys; print('\n'.join(sorted(json.load(sys.stdin).keys())))"

Common issues:

ErrorCauseSolution
ECONNREFUSEDPostgreSQL not reachableVerify hostname, port, and network policies
password authentication failedInvalid credentialsCheck postgres-password in the Secret referenced by backstage.secretName
database does not existMissing databaseCreate the database on PostgreSQL server
connection.filename is not supportedOutdated Backstage imageUpgrade to latest control plane chart

Next Steps