It is highly recommended to read the Design document to understand the components that make up Backstage.
The Backstage Operator supports three configuration levels: Default, Raw, and Custom Resource Config.
The Default Configuration defines the structure of all Backstage instances within a cluster. It consists of a set of YAML manifests that define Kubernetes resources for a Backstage instance. This configuration is located in the *-default-config
ConfigMap in the Backstage operator namespace (usually called backstage-system or backstage-operator) and mounted to the /default-config
directory of the Backstage controller container.
Key/File Name | Object Kind | Object Name | Mandatory | Multi | Version | Notes |
---|---|---|---|---|---|---|
deployment.yaml | appsv1.Deployment | backstage- | Yes | No | >=0.1.x | Backstage deployment |
service.yaml | corev1.Service | backstage- | Yes | No | >=0.1.x | Backstage Service |
db-statefulset.yaml | appsv1.StatefulSet | backstage-psql- | For local DB | No | >=0.1.x | PostgreSQL StatefulSet |
db-service.yaml | corev1.Service | backstage-psql- | For local DB | No | >=0.1.x | PostgreSQL Service |
db-secret.yaml | corev1.Secret | backstage-psql-secret- | For local DB | No | >=0.1.x | Secret to connect Backstage to PGSQL |
route.yaml | openshift.Route | backstage- | No (for OCP) | No | >=0.1.x | Route exposing Backstage service |
app-config.yaml | corev1.ConfigMap | backstage-appconfig- | No | No | >=0.2.x | Backstage app-config.yaml |
configmap-files.yaml | corev1.ConfigMap | backstage-files- | No | No | >=0.2.x | Backstage config file inclusions from configMap |
configmap-envs.yaml | corev1.ConfigMap | backstage-envs- | No | No | >=0.2.x | Backstage environment variables from ConfigMap |
secret-files.yaml | corev1.Secret | backstage-files- | No | No | >=0.2.x | Backstage config file inclusions from Secret |
secret-envs.yaml | corev1.Secret | backstage-envs- | No | No | >=0.2.x | Backstage environment variables from Secret |
dynamic-plugins.yaml | corev1.ConfigMap | backstage-dynamic-plugins- | No | No | >=0.2.x | Dynamic plugins configuration |
pvcs.yaml | corev1.PersistentVolumeClaim | backstage-<cr-name>-<pvc-name> | No | Yes | >=0.4.x | List of PVC objects to be mounted to Backstage container |
Meanings of "Mandatory" Column:
- Yes - Must be configured; deployment will fail otherwise.
- For local DB - Has to be configured if
spec.enableLocalDb
istrue
(or unset) in the Backstage CR. - No - Optional configuration.
- No (for OCP) - Optional configuration, working in Openshift only.
You can see examples of default configurations as part of the Operator Profiles in the default-config directory.
Some objects, such as: app-config, configmap-files, secret-files, dynamic-plugins, pvcs, are mounted to the Backstage Container as files or directories. Default mount path is Container's WorkingDir, if not defined it falls to "/opt/app-root/src".
Using rhdh.redhat.com/mount-path annotation it is possible to define the directory where PersistentVolumeClaim object will be mounted to Backstage Container.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
annotations:
rhdh.redhat.com/mount-path: /mount/path/from/annotation
...
In the example above the PVC called myclaim will be mounted to /mount/path/from/annotation directory
For Backstage to function consistently at runtime, certain metadata values need to be predictable. Therefore, the Operator generates values according to the following rules. Any value for these fields specified in either Default or Raw Configuration will be replaced by the generated values.
For All the objects metadata.name is generated according to the rules defined in the Default Configuration files, column Object name. means a Name of Backstage Custom Resource owning this configuration. For example, Backstage CR named mybackstage will create K8s Deployment resource called backstage-mybackstage. Specific, per-object generated metadata described below.
- deployment.yaml
spec.selector.matchLabels[rhdh.redhat.com/app] = backstage-<cr-name>
spec.template.metadata.labels[rhdh.redhat.com/app] = backstage-<cr-name>
- service.yaml
spec.selector[rhdh.redhat.com/app] = backstage-<cr-name>
- db-statefulset.yaml
spec.selector.matchLabels[rhdh.redhat.com/app] = backstage-psql-<cr-name>
spec.template.metadata.labels[rhdh.redhat.com/app] = backstage-psql-<cr-name>
- db-service.yaml
spec.selector[rhdh.redhat.com/app] = backstage-psql-<cr-name>
Since version 0.4.0, Operator supports multi objects which mean the object type(s) marked as Multi=true in the table above can be declared and added to the model as the list of objects of certain type. To do so multiple objects are added to the yaml file using "---" delimiter.
For example, adding the following code snip to pvcs.yaml will cause creating 2 PVCs called backstage-<cr-name>-myclaim1 and backstage-<cr-name>-myclaim2 and mounting them to Backstage container accordingly.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim1
...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim2
...
Raw Configuration consists of the same YAML manifests as the Default configuration, but is specific to each Custom Resource (CR). You can override any or all Default configuration keys (e.g., for deployment.yaml
) or add new keys not defined in the Default configuration by specifying them in ConfigMaps.
Here’s a fragment of the Backstage spec containing Raw configuration:
spec:
rawRuntimeConfig:
backstageConfig: <configMap-name> # to use for all manifests except db-*.yaml
localDbConfig: <configMap-name> # to use for db-*.yaml manifests
NOTE: While the Backstage Application config is separated from Database Configuration, it makes no difference which ConfigMap you use for which object; they are ultimately merged into one structure. Just avoid using the same keys in both.
The desired state of resources created by the Backstage Operator is defined in the Backstage Custom Resource Spec. Here’s an example of a simple Backstage CR:
apiVersion: rhdh.redhat.com/v1alpha2
kind: Backstage
metadata:
name: mybackstage
spec:
application:
appConfig:
configMaps:
- name: my-app-config
extraEnvs:
secrets:
- name: my-secrets
This Custom Resource defines a Backstage instance called mybackstage and also:
- Adds additional app-config stored in the my-app-config ConfigMap.
- Adds some extra environment variables stored (as key-value pairs) in the Secret called my-secrets.
For API version v1alpha2 (Operator version 0.3.x), the Backstage CR Spec contains the following top-level elements:
This section explains how the Backstage Application is configured inside the container.
As documented in the Backstage documentation, the Backstage application is configured with one or more app-config files, which are merged from the first to the last. The Operator can contribute to the app-config list by mounting the ConfigMap defined in default-config/app-config.yaml
, as specified in the Default Configuration section. Additionally, it is possible to define an array of external, user-created ConfigMaps located in the same namespace as the Backstage CR using spec.application.appConfig
.
For example, consider the following ConfigMaps containing Backstage app-config configuration:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
data:
my-app-config.yaml: |
# Some fragment of app-config here
This app-config file can be mounted to the Backstage container as follows:
spec:
application:
appConfig:
mountPath: /my/path
configMaps:
- name: my-app-config
In this example, additional app-config is defined in the ConfigMaps called my-app-config, and it is mounted as YAML files to the /my/path
directory in the Backstage container:
/my/path/my-app-config.yaml
The ConfigMap key/value defines the file name and content, and this app-config will be applied as the last in the -app-config arguments sequence. Therefore, as per this example, the ConfigMap should be created in the namespace as a prerequisite. Then the Operator will create the file /my/path/my-app-config.yaml
and add it to the end of the Backstage command-line arguments as follows:
--config /my/path/my-app-config.yaml
Note: It is possible to define several app-config files inside one ConfigMap (even if there are no visible reasons for it) but since it is a Map, the order of how they are applied is not guaranteed. On the other hand, Backstage application merges the chain of app-config files from first to last, so order is important. Taking this into account, keeping several app-config files inside one ConfigMap is NOT recommended. For this case consider defining several one-entry ConfigMaps instead.
Includes and Dynamic Data (including extra files and extra environment variables) support configuring additional ConfigMaps and Secrets.
Extra files can be mounted to the Backstage container from pre-created ConfigMaps or Secrets. For example, consider the following objects in the namespace:
apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
data:
file11.txt: |
My file11 content
file12.txt: |
My file12 content
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
data:
file21.txt: |
My file21 content
file22.txt: |
My file22 content
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm3
data:
file31.txt: |
My file31 content
file32.txt: |
My file32 content
---
apiVersion: v1
kind: Secret
metadata:
name: secret1
stringData:
secret11.txt: |
secret-content
---
apiVersion: v1
kind: Secret
metadata:
name: secret2
stringData:
secret21.txt: |
secret-content
secret22.txt: |
secret-content
These objects can be mounted to the Backstage container as follows:
spec:
application:
extraFiles:
mountPath: /my/path
configMaps:
- name: cm1
- name: cm2
key: file21.txt
- name: cm3
mountPath: /my/cm3/path
secrets:
- name: secret1
key: file3.txt
- name: secret2
mountPath: /my/secret2/path
The Operator will either get all entries from the specified object (if no key is specified) or will pick the specified one, creating volumes per object and mounting the files to the Backstage container. Since v1alpha3 (v0.4) Backstage CRD introduced mountPath field which allows to mount ConfigMap or Secret to specified path. A combination of key/mountPath fields impacts on whether the Volume will be mounted with or without subPath as following:
- If nothing specified: each key/values will be mounted as filename/content with subPath
- If key specified, with or without mountPath: the specified key/value will be mounted with subPath
- If only mountPath specified: a directory containing all the key/value will be mounted without subPath
Note: A volume mounted with subPath is not automatically updated by Kubernetes. So, by default, the Operator watches such ConfigMaps/Secrets and refreshes the Backstage Pod when they change.
Note: To limit read access to Secrets by the Operator Service Account (for security reasons), we do not support mounting files from Secrets without mountPath and key specified.
In our example, the following files will be mounted:
// Each file is mounted individually (related resources are watched by Operator):
/my/path/
file11.txt
file12.txt
file21.txt
file3.txt
// Directory mounted (related resources are auto-updated):
/my/cm3/path/
file31.txt
file32.txt
/my/secret2/path/
file1
file2
Since v1alpha3 (Operator version 0.4.0), it is also possible to mount directory from pre-created PersistentVolumeClaim using spec.application.extraFiles.pvcs field. PersistentVolumeClaims are mounted as a directory to the container's path defined as following:
- spec.application.extraFiles.pvcs[].mountPath if defined
- Then spec.application.extraFiles.mountPath/ if defined
- Then Backstage container's WorkingDir if defined
- And if nothing defined it falls to default path (/opt/app-root/src)
For example, consider the following objects in the namespace:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim2
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
These objects can be mounted to the Backstage container as follows:
spec:
application:
extraFiles:
mountPath: /my/path
pvcs:
- name: myclaim1
- name: myclaim2
mountPath: /vol/my/claim
As a result, the following directories will be mounted:
/my/path/myclaim1
/vol/my/claim
Extra environment variables can be injected into the Backstage container from pre-created ConfigMaps or Secrets, as well as specified directly in the Custom Resource. For instance, consider the following objects in the namespace:
apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
data:
ENV_VAR1: "1"
ENV_VAR2: "2"
---
apiVersion: v1
kind: Secret
metadata:
name: secret1
data:
ENV_VAR3: "base64encoded3"
ENV_VAR4: "base64encoded4"
In addition, we may want to create the environment variable MY_VAR=my-value
by declaring it directly in the Custom Resource:
spec:
application:
extraEnvs:
configMaps:
- name: cm1
key: ENV_VAR1
secrets:
- name: secret1
envs:
- name: MY_VAR
value: "my-value"
Similar to extraFiles, you can specify a key name to inject only a particular environment variable. In our example, the following environment variables will be injected:
ENV_VAR1 = 1
ENV_VAR3 = 3
ENV_VAR4 = 4
MY_VAR = my-value
The Operator can configure Dynamic Plugins. To support Dynamic Plugins, the Backstage deployment should contain a dedicated initContainer called install-dynamic-plugins (see RHDH deployment.yaml). To enable the Operator to configure Dynamic Plugins for a specific Backstage instance (CR), the user must create a ConfigMap with an entry called dynamic-plugins.yaml.
For example, the dynamic-plugins-config ConfigMap contains a simple Dynamic Plugins configuration, which includes predefined default plugins in dynamic-plugins.default.yaml and the GitHub plugin provided in the package located at ./dynamic-plugins/dist/backstage-plugin-catalog-backend-module-github-dynamic
.
kind: ConfigMap
apiVersion: v1
metadata:
name: dynamic-plugins-config
data:
dynamic-plugins.yaml: |
includes:
- dynamic-plugins.default.yaml
plugins:
- package: './dynamic-plugins/dist/backstage-plugin-catalog-backend-module-github-dynamic'
disabled: false
pluginConfig:
catalog:
providers:
github:
organization: "${GITHUB_ORG}"
schedule:
frequency: { minutes: 1 }
timeout: { minutes: 1 }
initialDelay: { seconds: 100 }
To configure it with the Backstage CR, the following spec should be included:
spec:
application:
dynamicPluginsConfigMapName: "dynamic-plugins-config"
NOTE: These fields are deprecated for versions >= 0.3.0 in favor of the spec.deployment.
spec:
application:
image: 'Backstage container image: [tag], e.g., "quay.io/my/my-rhdh:latest"'
replicas: number of replicas, e.g., 2
imagePullSecrets: array of image pull secrets names, e.g.,
- my-secret-name
To support Backstage service routing on OpenShift, the Operator can create a route.openshift.io
resource, as specified in the spec.application.route field. Here’s an example:
spec:
application:
route:
enabled: true
host: my.host
subdomain: mysubdomain
tls:
certificate: "certificatecontent"
externalCertificateSecretName: "my-certificate"
key: "keycontent"
caCertificate: "caCertificatecontent"
Here, the user can specify some of the OpenShift Route specifications fields. The names of the Backstage spec.application.route
fields correspond to the names of Route specifications fields and follow the same default rules if not specified.
Also note that securing Routes with external certificates in TLS secrets (via the spec.application.route.tls.externalCertificateSecretName
CR field) is a Technology Preview feature in OpenShift. It requires enabling the RouteExternalCertificate
OpenShift Feature Gate and might not be functionally complete. See Creating a route with externally managed certificate for more details.
Since v1alpha2 (Operator version 0.3.0), the Backstage CRD contains spec.deployment, which allows for patching the Backstage Deployment resource with fields defined in spec.deployment.patch
, which contains a fragment of the apps.Deployment
object. This pathcing is performed via strategic merge patch using Kustomize's library.
For example, the following specification fragment will:
- Set an additional volume named my-volume and mount it to /my/path of the Backstage container.
- Replace existed dynamic-plugins-root volume
- Set the CPU request for Backstage containers to 250m.
- Set the label my=true for the Backstage Pod.
- Adds another my-sidecar container
Note: It is assumed that the Backstage container name as defined in the default configuration is backstage-backend.
spec:
deployment:
patch:
spec:
template:
metadata:
labels:
my: true
spec:
containers:
- name: backstage-backend
volumeMounts:
- mountPath: /my/path
name: my-volume
resources:
requests:
cpu: 250m
- name: my-sidecar
image: quay.io/my-org/my-sidecar:latest
volumes:
- ephemeral:
volumeClaimTemplate:
spec:
storageClassName: "special"
name: my-volume
- $patch: replace
name: dynamic-plugins-root
persistentVolumeClaim:
claimName: dynamic-plugins-root
Backstage uses PostgreSQL as a storage solution. The Operator can:
- Pre-create and manage a local database (i.e., created as a StatefulSet in the same namespace as the Backstage Deployment).
- Allow Backstage to use an external PostgreSQL database.
This is dictated by the following configuration:
spec:
database:
enableLocalDb: [true]|false
If local DB is enabled (which is simpler but not recommended for production), the Operator will:
- Create a StatefulSet according to the template defined in either the default or raw
db-statefulset.yaml
. - Create a Service for accessing the DB.
- Generate a password for this DB.
- Inject corresponding environment variables containing DB connection information (host, port, username, password) into the Backstage container.
If local DB is disabled (enableLocalDb: false
), then the secret with DB connection information must be created manually and specified in either spec.database.authSecretName or one of spec.application.extraEnvs.secrets.
For more information, refer to the External DB Integration manual.