Configuration Files
Overlay Documentation
This page covers writing tests within rhdh-plugin-export-overlays. For using @red-hat-developer-hub/e2e-test-utils in external projects, see the Guide.
This page explains the YAML configuration files used in overlay E2E tests.
If you're looking for the general file formats and examples, see Configuration Files (Guide). This page focuses on overlay-specific behavior such as metadata auto-generation and OCI URL replacement.
All Configuration Files Are Optional
Key Concept
All configuration files in tests/config/ are optional. The package provides sensible defaults. Only create a configuration file when you need to override or extend the defaults (i.e., when the default config does not already cover your use case).
If your plugin works with the package defaults and metadata-based configuration, you may not need any configuration files at all.
Configuration File Location
Configuration files are placed in tests/config/:
tests/config/
├── app-config-rhdh.yaml # RHDH application configuration (optional)
├── rhdh-secrets.yaml # Kubernetes secrets (optional)
├── dynamic-plugins.yaml # Dynamic plugins (optional - usually not needed)
├── value_file.yaml # Helm values override (optional, Helm only)
├── value_file-app-next.yaml # Extra Helm values when new frontend system is active (optional)
└── subscription.yaml # Operator subscription (optional, Operator only)2
3
4
5
6
7
All of these files are optional. Only create them when you need to override or extend defaults.
useNewFrontendSystem (app-next / NFS)
When the new frontend system is enabled (see configure() and New frontend system), @red-hat-developer-hub/e2e-test-utils merges:
- Secrets —
APP_CONFIG_app_packageName=app-nextandENABLE_STANDARD_MODULE_FEDERATION=trueinto therhdh-secretsSecret (merged with common/auth/workspace layers beforeenvsubst; you no longer need duplicate keys in a separaterhdh-secrets-next.yamlfor that). - Dynamic plugins — Default OCI entries for app-auth and app-integrations as package defaults; override versions in
tests/config/dynamic-plugins.yaml(guide).
You can remove hand-maintained app-auth / app-integrations lines from dynamic-plugins.yaml when the framework defaults match your train.
Optional value_file-app-next.yaml is merged last when the new frontend system is active and the file exists — use for chart tweaks specific to app-next runs.
app-config-rhdh.yaml (Optional)
The main RHDH configuration file. This file is merged with default configurations from @red-hat-developer-hub/e2e-test-utils.
Only create this file when you need to:
- Override a default value in the RHDH app config
- Add config keys that are not provided by the defaults
Purpose
- Set plugin-specific configuration values
- Configure backend settings
- Customize the RHDH instance title
- Allow external hosts for reading
Structure
# RHDH app config file
# This file merges with the default values from @red-hat-developer-hub/e2e-test-utils
app:
title: RHDH <Plugin Name> Test Instance
backend:
reading:
allow:
- host: ${EXTERNAL_HOST}
# Plugin-specific configuration
<pluginName>:
setting1: value1
setting2: value22
3
4
5
6
7
8
9
10
11
12
13
14
15
Referencing Secrets
Use ${VAR_NAME} syntax to reference values from rhdh-secrets.yaml:
backend:
reading:
allow:
- host: ${TECH_RADAR_DATA_URL}
techRadar:
url: "http://${TECH_RADAR_DATA_URL}/tech-radar"2
3
4
5
6
These reference the Kubernetes Secret created by rhdh-secrets.yaml. The secret must define TECH_RADAR_DATA_URL for this to work.
Real Example: Tech Radar
# rhdh app config file
# this file is used to merge with the default values of the rhdh app config
app:
title: RHDH Tech Radar Test Instance
backend:
reading:
allow:
- host: ${TECH_RADAR_DATA_URL}
techRadar:
url: "http://${TECH_RADAR_DATA_URL}/tech-radar"2
3
4
5
6
7
8
9
10
11
rhdh-secrets.yaml (Optional)
A Kubernetes Secret manifest that bridges environment variables to the RHDH deployment.
Only create this file when you need to pass environment variables into RHDH configuration files.
Environment Variable Substitution
Important
Environment variable substitution is performed ONLY on rhdh-secrets.yaml.
When this file is processed, any $VAR_NAME references are replaced with actual values from the environment. Other config files (app-config-rhdh.yaml, dynamic-plugins.yaml) do not get direct substitution - they reference the secrets created by this file.
When Is This File Needed?
| Where you need the variable | rhdh-secrets.yaml required? |
|---|---|
Test code (*.spec.ts) | No - use process.env directly |
RHDH configs (app-config-rhdh.yaml, etc.) | Yes |
How It Works
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Vault / .env │────▶│ rhdh-secrets.yaml│────▶│ app-config-rhdh.yaml│
│ MY_SECRET=value │ │ MY_SECRET: $VAR │ │ ${MY_SECRET} │
│ │ │ (substituted) │ │ (references secret) │
└─────────────────┘ └──────────────────┘ └─────────────────────┘2
3
4
5
- Environment variable exists (from Vault in CI, or
.envlocally) rhdh-secrets.yamlreferences it with$VAR_NAME- substituted with actual value- RHDH configs reference the secret with
${VAR_NAME}
Structure
apiVersion: v1
kind: Secret
metadata:
name: rhdh-secrets
type: Opaque
stringData:
# $VAR_NAME is replaced with the actual value from environment
TECH_RADAR_DATA_URL: $TECH_RADAR_DATA_URL
API_KEY: $VAULT_MY_API_KEY2
3
4
5
6
7
8
9
Real Example: Tech Radar
apiVersion: v1
kind: Secret
metadata:
name: rhdh-secrets
type: Opaque
stringData:
TECH_RADAR_DATA_URL: $TECH_RADAR_DATA_URL2
3
4
5
6
7
Then in app-config-rhdh.yaml, you can use ${TECH_RADAR_DATA_URL}:
techRadar:
url: "http://${TECH_RADAR_DATA_URL}/tech-radar"2
dynamic-plugins.yaml (Optional)
Configuration for dynamic plugins. This file is optional and in most cases should not be provided.
Best Practice
Do NOT create dynamic-plugins.yaml unless you have a specific reason. When the file doesn't exist, all plugins in the workspace are automatically enabled with their default configurations from metadata. Only create this file if you need to override specific plugin settings.
How Plugin Configuration is Generated
The package uses plugin metadata files (in metadata/*.yaml at the workspace level) to automatically configure plugins:
workspaces/<plugin-name>/
├── metadata/ # Plugin metadata (Package CRD format)
│ ├── plugin-frontend.yaml # Frontend plugin metadata
│ └── plugin-backend.yaml # Backend plugin metadata
├── e2e-tests/ # Your test project
│ └── tests/config/
│ └── dynamic-plugins.yaml # Optional - usually not needed
└── plugins/ # Plugin source code2
3
4
5
6
7
8
The metadata files contain spec.appConfigExamples with the plugin's default configuration. These are automatically read and used during deployment.
When File Doesn't Exist (Recommended)
When tests/config/dynamic-plugins.yaml is missing, the package auto-generates a complete configuration:
- Scans for plugin metadata in
../metadata/(relative to e2e-tests) - Reads each
*.yamlfile as Package CRD format - Extracts
spec.dynamicArtifactandspec.appConfigExamples[0].content - Generates plugin entries with:
package: The dynamicArtifact pathdisabled: false(enabled by default)pluginConfig: From appConfigExamples
- Merges with package defaults and auth config
Result: All plugins from metadata are enabled with their default configurations. This is the recommended approach for most plugins.
When File Exists
When tests/config/dynamic-plugins.yaml exists:
- Package defaults are loaded
- Auth-specific plugins (keycloak/guest) are merged
- Your custom config is merged on top (overrides earlier values)
- Plugin metadata is injected only for plugins listed in your file
Important: If you provide this file, you take control of which plugins are enabled. Plugins not listed in your file won't get metadata injected automatically.
When to Create This File
Only create this file when:
- You need to disable specific plugins
- You need to override default plugin settings from metadata
- The plugin has no metadata files
- You need different configuration than the metadata defaults
Structure (If Needed)
plugins:
- package: "@company/backstage-plugin-example"
disabled: false
pluginConfig:
example:
setting: customValue # Overrides metadata default2
3
4
5
6
See Also
For detailed information about plugin metadata handling, see the package documentation:
value_file.yaml (Optional, Helm Only)
Override Helm chart values for RHDH deployment.
Only create this file when you need to:
- Override default Helm values
- Configure cluster-specific settings
- Customize resource limits or replicas
Structure
# Override Helm values
upstream:
backstage:
resources:
limits:
memory: 4Gi2
3
4
5
6
This file is merged with the package's default Helm values.
subscription.yaml (Optional, Operator Only)
Override Operator subscription settings.
Only create this file when you need to:
- Use a specific operator channel
- Configure operator-specific settings
- Override default subscription configuration
Structure
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: rhdh
spec:
channel: fast2
3
4
5
6
This file is merged with the package's default subscription configuration.
Plugin Package Resolution
Plugin package references in dynamic-plugins.yaml are automatically resolved before deployment. The framework uses workspace metadata/*.yaml files as the source of truth — whatever spec.dynamicArtifact says (OCI ref or wrapper path), that's what the plugin resolves to.
The resolution behaves differently in PR, nightly, and local dev modes. For the complete resolution logic with concrete input/output examples, see Plugin Metadata Resolution.
Configuration Merging
@red-hat-developer-hub/e2e-test-utils merges your configuration with defaults in this order:
- Base defaults from
@red-hat-developer-hub/e2e-test-utils/config/common/ - Auth-specific from
@red-hat-developer-hub/e2e-test-utils/config/auth/{guest,keycloak}/ - Deployment method from
@red-hat-developer-hub/e2e-test-utils/config/{helm,operator}/ - Your custom config from
tests/config/
Later files override earlier ones using deep merge.
Merge Strategies
Arrays use the "replace" strategy by default:
# Base config
backend:
reading:
allow:
- host: localhost
# Your config (replaces the array)
backend:
reading:
allow:
- host: ${EXTERNAL_HOST}
# Result
backend:
reading:
allow:
- host: ${EXTERNAL_HOST} # localhost is replaced2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Setting Environment Variables
Environment variables can be set in multiple ways:
1. In Test Code
Set variables in beforeAll:
test.beforeAll(async ({ rhdh }) => {
// Deploy external service first
await $`bash ${setupScript} ${project}`;
// Get the service URL and set as env var
process.env.TECH_RADAR_DATA_URL = await getServiceUrl();
// Now deploy RHDH (will use the env var)
await rhdh.deploy();
});2
3
4
5
6
7
8
9
10
2. In .env File
For local development:
# .env
TECH_RADAR_DATA_URL=my-service.example.com2
3. In Vault (CI)
Add secrets to the Vault with VAULT_ prefix. They are automatically exported during OpenShift CI execution:
VAULT_TECH_RADAR_DATA_URL: my-service.apps.cluster.example.comThen reference in your config:
techRadar:
url: "http://${VAULT_TECH_RADAR_DATA_URL}/tech-radar"2
Secret Naming
All secrets in Vault must start with VAULT_ prefix for automatic export.
Common Configuration Patterns
Allow External Hosts
backend:
reading:
allow:
- host: ${EXTERNAL_HOST}2
3
4
Set Plugin URL
<pluginName>:
url: "http://${SERVICE_URL}/<endpoint>"2
Custom App Title
app:
title: RHDH <Plugin> Test Instance2
Real-World Workspace Patterns
These examples show how different workspaces use dynamic-plugins.yaml and how the package resolves their plugins.
Auto-Generated (No dynamic-plugins.yaml)
Workspaces: tech-radar, quickstart, acr
When no dynamic-plugins.yaml exists, the package auto-generates entries from all metadata/*.yaml files:
# Auto-generated at deploy time:
plugins:
- package: oci://ghcr.io/.../backstage-community-plugin-tech-radar:bs_1.45.3__1.13.0
disabled: false
# pluginConfig injected from metadata appConfigExamples (PR/local mode)2
3
4
5
No configuration files needed — metadata provides everything.
Cross-Workspace Plugins
Workspace: argocd
When your workspace needs plugins from another workspace (e.g., Kubernetes backend for ArgoCD):
plugins:
# Workspace plugin — resolved to metadata OCI ref (or PR tag)
- package: oci://ghcr.io/.../backstage-community-plugin-argocd:bs_1.45.3__2.4.3!backstage-community-plugin-argocd
pluginConfig:
dynamicPlugins:
frontend: { ... }
# Cross-workspace plugin — no metadata match, kept as-is
- package: ./dynamic-plugins/dist/backstage-plugin-kubernetes-backend-dynamic2
3
4
5
6
7
8
9
Cross-workspace plugins have no metadata in the current workspace, so they pass through unchanged in all modes.
OCI Aliases
Workspace: redhat-resource-optimization
Some plugins share a single OCI image with multiple plugins distinguished by aliases (the !alias suffix):
plugins:
- package: oci://quay.io/redhat-resource-optimization/dynamic-plugins:1.3.2!red-hat-developer-hub-plugin-redhat-resource-optimization2
The alias after ! tells RHDH which plugin to extract from the shared image.
Disabled Wrapper Plugins
Workspace: scorecard
When your workspace uses an OCI image for a plugin that also has a local wrapper enabled by default:
plugins:
# Workspace plugin — resolved to metadata ref
- package: oci://ghcr.io/.../red-hat-developer-hub-backstage-plugin-scorecard:tag!alias
# Cross-workspace OCI — kept as-is
- package: oci://ghcr.io/.../red-hat-developer-hub-backstage-plugin-dynamic-home-page:tag!alias
# Disable the local wrapper to avoid conflicts
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-dynamic-home-page
disabled: true2
3
4
5
6
7
8
9
10
Different OCI Registries
Plugins can come from different registries. The package preserves the original registry from each plugin's metadata:
# ghcr.io (community plugins)
- package: oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tech-radar:bs_1.45.3__1.13.0
# quay.io (Red Hat plugins)
- package: oci://quay.io/rhdh/red-hat-developer-hub-backstage-plugin-scaffolder-relation-processor@sha256:abc123
# registry.access.redhat.com (certified plugins)
- package: oci://registry.access.redhat.com/rhdh/red-hat-developer-hub-backstage-plugin-orchestrator@sha256:f40d39fb2
3
4
5
6
7
8
npm Packages
Workspace: global-header
For plugins published to npm instead of OCI:
plugins:
- package: "@red-hat-developer-hub/backstage-plugin-global-header-test@0.0.2"
integrity: "sha512-ABC123..."2
3
npm packages with integrity hashes pass through unchanged in all modes — no metadata resolution.
All Local Paths
Workspace: topology
Some workspaces use only local paths (no OCI references in their config):
plugins:
- package: ./dynamic-plugins/dist/backstage-community-plugin-topology
- package: ./dynamic-plugins/dist/backstage-plugin-kubernetes-backend-dynamic2
3
In PR mode, pluginConfig from metadata is injected. In nightly mode, local paths are resolved to metadata OCI refs if metadata exists. Local paths with no metadata match stay unchanged.
Related Pages
- Directory Layout - Where config files go
- Spec Files - Using config in tests
- Environment Variables - All supported variables
- Plugin Metadata Resolution - How package resolution works in PR, nightly, and local modes
- Local OCI Testing - Testing with PR-built OCI images locally