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:
| Client | Grant type | Purpose |
|---|---|---|
| Sign-in client | authorization_code | User login via browser |
| Service client | client_credentials | Background 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:
- Grant Types:
authorization_code - Token Format: JWT tokens (not opaque tokens)
- Redirect URLs: Add the Backstage callback URL:
<protocol>://<backstage-domain>/api/auth/openchoreo-auth/handler/frame
- User Claims: Configure the access token to include:
family_name,given_name,email,groups
For the service client:
- Grant Types:
client_credentials - 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: ""
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:
| Field | Description | Default |
|---|---|---|
backstage.auth.oidcScope | Space-separated scopes requested during user login via the authorization_code flow (sign-in client) | "openid profile email" |
backstage.auth.scope | Space-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
| Flag | Description | Default |
|---|---|---|
backstage.features.workflows.enabled | Show Workflows tab and overview card. Requires Workflow Plane. | true |
backstage.features.observability.enabled | Show Metrics, Traces, Runtime Logs tabs and runtime health card. Requires Observability Plane. | true |
backstage.features.auth.redirectFlow.enabled | Silently redirect to the IdP instead of showing the Backstage sign-in popup. | true |
backstage.features.assistant.enabled | Enable the "Ask Perch" chat affordance. Requires the perch-agent service to be deployed. | false |
backstage.features.assistant.perchAgentUrl | Upstream URL for the perch-agent. Leave empty to default to the in-cluster Service in the release namespace. | "" |
features.secretManagement.enabled | Enable 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
| Parameter | Description | Default |
|---|---|---|
backstage.catalogSync.frequency | Seconds between periodic full syncs. Lower the value when eventForwarder.enabled is false. | 300 |
eventForwarder.enabled | Enables 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.
| Variable | Description | Source |
|---|---|---|
NODE_ENV | Node.js environment | backstage.env (default: production) |
LOG_LEVEL | Logging verbosity | backstage.env (default: info) |
PORT | HTTP server port | backstage.env (default: 7007) |
BACKSTAGE_BASE_URL | Public URL for OAuth redirects and links | backstage.baseUrl |
OPENCHOREO_API_URL | Internal OpenChoreo API endpoint | backstage.openchoreoApi.url (auto-configured) |
BACKEND_SECRET | Session encryption key | from Secret (backend-secret) |
OPENCHOREO_AUTH_CLIENT_ID | Sign-in client ID (authorization_code flow) | backstage.auth.clientId |
OPENCHOREO_AUTH_CLIENT_SECRET | Sign-in client secret | from Secret (client-secret) |
OPENCHOREO_SERVICE_CLIENT_ID | Service client ID (client_credentials flow — background tasks) | backstage.auth.serviceClientId (falls back to clientId) |
OPENCHOREO_SERVICE_CLIENT_SECRET | Service client secret | from Secret (key backstage.auth.serviceClientSecretKey, default: client-secret) |
OPENCHOREO_AUTH_METADATA_URL | OIDC discovery URL (omitted when unset) | security.oidc.wellKnownEndpoint |
OPENCHOREO_AUTH_AUTHORIZATION_URL | OAuth authorization endpoint | security.oidc.authorizationUrl |
OPENCHOREO_AUTH_TOKEN_URL | OAuth token endpoint (shared by both clients) | security.oidc.tokenUrl |
OPENCHOREO_AUTH_OIDC_SCOPE | OIDC scopes for the sign-in client | backstage.auth.oidcScope (default: openid profile email) |
OPENCHOREO_AUTH_SCOPE | Scopes for the service client's client_credentials token request | backstage.auth.scope (default: "") |
OPENCHOREO_FEATURES_AUTH_ENABLED | Enable authentication | security.enabled |
OPENCHOREO_FEATURES_AUTHZ_ENABLED | Enable authorization | security.authz.enabled |
OPENCHOREO_FEATURES_AUTH_REDIRECT_FLOW_ENABLED | Silent redirect vs sign-in popup | backstage.features.auth.redirectFlow.enabled |
OPENCHOREO_FEATURES_WORKFLOWS_ENABLED | Show Workflows UI | backstage.features.workflows.enabled |
OPENCHOREO_FEATURES_OBSERVABILITY_ENABLED | Show Metrics/Traces/Logs UI | backstage.features.observability.enabled |
OPENCHOREO_FEATURES_ASSISTANT_ENABLED | Enable the "Ask Perch" chat affordance | backstage.features.assistant.enabled |
OPENCHOREO_PERCH_AGENT_URL | Upstream URL for the perch-agent (in-cluster fallback applied) | backstage.features.assistant.perchAgentUrl |
OPENCHOREO_FEATURES_SECRET_MANAGEMENT_ENABLED | Enable the Secret management UI | features.secretManagement.enabled (top-level) |
OPENCHOREO_EVENTS_ENABLED | Enable event-driven catalog sync | eventForwarder.enabled (top-level) |
OPENCHOREO_CATALOG_SYNC_FREQUENCY | Periodic full-sync interval (seconds) | backstage.catalogSync.frequency |
JENKINS_BASE_URL | Jenkins server base URL (consumed when Jenkins integration is enabled) | backstage.externalCI.jenkins.baseUrl |
JENKINS_USERNAME | Jenkins username for API authentication | backstage.externalCI.jenkins.username |
JENKINS_API_KEY | Jenkins API key | from Secret (jenkins-api-key) |
GITHUB_HOST | GitHub host for the Actions integration (github.com or a GHES host) | backstage.externalCI.githubActions.host |
GITHUB_API_BASE_URL | GitHub API base URL (set for GHES, e.g. https://ghe.example.com/api/v3) | backstage.externalCI.githubActions.apiBaseUrl |
GITHUB_TOKEN | GitHub backend token (catalog, scaffolder, TechDocs — not the Actions card) | from Secret (github-actions-token, optional) |
AUTH_GITHUB_CLIENT_ID | GitHub OAuth App client ID for per-user sign-in (powers the Actions card) | backstage.externalCI.githubActions.oauth.clientId |
AUTH_GITHUB_CLIENT_SECRET | GitHub OAuth App client secret | from Secret (github-oauth-client-secret, optional) |
DATABASE_CLIENT | Database driver (better-sqlite3 or pg) | backstage.database.type |
SQLITE_STORAGE_DIR | SQLite database directory (when using SQLite) | backstage.database.sqlite.mountPath |
POSTGRES_HOST / _PORT / _USER / _PASSWORD / _DB | PostgreSQL connection fields (when database.type=postgresql) | from Secret (postgres-host, postgres-port, postgres-user, postgres-password, postgres-db) |
PGSSLMODE | Set to require when PostgreSQL SSL is enabled | backstage.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"
| Parameter | Description | Default |
|---|---|---|
backstage.externalCI.jenkins.enabled | Toggle Jenkins integration | false |
backstage.externalCI.jenkins.baseUrl | Jenkins server base URL | "https://jenkins.example.com" |
backstage.externalCI.jenkins.username | Jenkins 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)
| Parameter | Description | Default |
|---|---|---|
backstage.externalCI.githubActions.enabled | Toggle GitHub Actions integration | false |
backstage.externalCI.githubActions.host | GitHub host (github.com or a GHES host) | "github.com" |
backstage.externalCI.githubActions.apiBaseUrl | GitHub API base URL (required for GHES, e.g. https://ghe.example.com/api/v3) | "" |
backstage.externalCI.githubActions.oauth.clientId | GitHub OAuth App client ID for the per-user sign-in the Actions card uses | "" |
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
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.
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 keyclient-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 toclient-secret.github-oauth-client-secret: GitHub OAuth App client secret. Required only when the GitHub Actions card is enabled (backstage.externalCI.githubActions.oauth.clientIdset) — see the GitHub Actions walkthrough.github-actions-token: GitHub backend token for catalog ingestion, the scaffolder, and TechDocs. Declaredoptional: trueon 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-hostpostgres-portpostgres-userpostgres-passwordpostgres-db
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:
| Backend | Use Case | Persistence | Scalability |
|---|---|---|---|
| SQLite (default) | Development, single-replica | Optional (emptyDir or PVC) | Single replica only |
| PostgreSQL | Production, multi-replica | External database | Horizontal 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
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
- Provision a PostgreSQL Database: Use a managed service (AWS RDS, Google Cloud SQL, Azure Database for PostgreSQL) or a self-hosted PostgreSQL instance
- Create a Database: Create a database for Backstage (e.g.,
backstage) - 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
- External PostgreSQL
- In-Cluster PostgreSQL
- Values File
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
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.
For PostgreSQL running within the same Kubernetes cluster:
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.your-namespace.svc.cluster.local" \
--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
Using a values file for better secret management:
# backstage-db-values.yaml
backstage:
secretName: "backstage-secrets"
database:
type: postgresql
postgresql:
ssl: true
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 \
-f backstage-db-values.yaml
PostgreSQL Configuration Reference
| Parameter | Description | Default |
|---|---|---|
backstage.database.type | Database backend (sqlite or postgresql) | sqlite |
backstage.database.postgresql.ssl | Enable SSL for PostgreSQL connections (sets PGSSLMODE=require) | false |
backstage.secretName | Secret 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:
| Check | Endpoint | Default |
|---|---|---|
| Liveness | /healthcheck | Enabled |
| Readiness | /healthcheck | Enabled |
Example Configurations
- Development
- Production
- API-only Mode
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
backstage:
enabled: true
replicas: 2
baseUrl: "https://console.example.com"
secretName: "backstage-secrets"
http:
hostnames:
- console.example.com
# PostgreSQL for production (required for multi-replica)
database:
type: postgresql
postgresql:
ssl: true
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 70
features:
workflows:
enabled: true
observability:
enabled: true
For headless deployments where only the API is needed:
backstage:
enabled: false
# Security can also be disabled for development
security:
enabled: false
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:
| Error | Cause | Solution |
|---|---|---|
ECONNREFUSED | PostgreSQL not reachable | Verify hostname, port, and network policies |
password authentication failed | Invalid credentials | Check postgres-password in the Secret referenced by backstage.secretName |
database does not exist | Missing database | Create the database on PostgreSQL server |
connection.filename is not supported | Outdated Backstage image | Upgrade to latest control plane chart |
Next Steps
- Identity Provider Configuration: Configure an external IdP
- Deployment Topology: Set up organizations and environments
- Helm Charts Reference: Complete Backstage configuration options