Upgrading your internal developer platform shouldn't feel like navigating a minefield. Upcoming Red Hat Developer Hub release 2.1 introduces major performance and architectural updates—including upstream Backstage's new front-end system and standalone OCI plugin images—but getting there requires rethinking how your custom plug-ins are built and mounted.
Two architectural shifts require preparation before upgrading: adopting the new front-end system and migrating plug-ins to standalone OCI images. Red Hat Developer Hub 2.1 includes other breaking changes not covered here; the upcoming release notes and migration documentation provide a complete list.
Important
Update custom front-end plug-ins and dynamic configurations before upgrading to Red Hat Developer Hub 2.1. Older plug-ins that rely on Scalprum mount points cannot load in the 2.1 application shell and will cause the deployment to fail on startup.
Whether you are a platform administrator managing Helm values and Operator configurations or a plugin author maintaining TypeScript blueprints, this guide covers what changed, how configuration maps to the new extension model, and how to test your setup with early 2.1 builds.
The new front-end system: Why you must update plug-ins first
In Red Hat Developer Hub 1.10, you could switch an instance between the old front-end system (packages/app) and the new front-end system (packages/app-next) using environment flags:
APP_CONFIG_app_packageName=app-next
ENABLE_STANDARD_MODULE_FEDERATION=trueThat toggle lets teams test their plug-ins against the new system while keeping the default setup intact.
In Red Hat Developer Hub 2.1, the previous front-end system and Scalprum—the module federation framework used to dynamically inject plug-ins at runtime—have been removed. packages/app-next is now the default packages/app shell. lugins that rely on dynamic Scalprum mount points (dynamicPlugins.frontend) will fail to mount in the browser.
Removing the old front-end system eliminates roughly 40,000 lines of legacy code, cuts image sizes, and aligns Red Hat Developer Hub directly with upstream Backstage extension blueprints.
Recommended custom plugin migration workflow (plugin authors)
When migrating custom plug-ins, the Red Hat Developer Hub engineering team follows this 6-step process:
- Discover: Check
package.json,src/plugin.ts, routes, API factories, components, and current dynamic plugin mount points to catalog what the plug-in exports. - Choose the alpha export: Use an
./alphaexport so the plug-in supports the new front-end system while keeping its root export (.) working for 1.10 instances. - Migrate extensions: Convert React components to Backstage extension blueprints (
PageBlueprint,EntityCardBlueprint,EntityContentBlueprint). - Update
package.json: Add the./alphaexport map alongside the root export. - Update local dev wiring: Add a new front-end system dev app entry point (
dev/index.tsxordev/nfs.tsx) using@backstage/frontend-defaultsto test the plug-in locally. - Verify: Run
yarn tscfrom your workspace root and run a build to check types and exports.
What changed for plugin authors
In the old front-end system (in Red Hat Developer Hub version 1.10 and earlier), plug-ins exported standard React components (PluginRoot), and platform engineers wired those components into UI slots using YAML mount points.
In the new front-end system (in Red Hat Developer Hub 1.10 onwards), plug-ins define their own placement in code through extension blueprints:
// src/alpha.tsx — entry point for the new frontend system
import { createFrontendPlugin, PageBlueprint } from '@backstage/frontend-plugin-api';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
import { rootRouteRef } from './routes';
const myPage = PageBlueprint.make({
params: {
path: '/my-tools',
routeRef: rootRouteRef,
title: 'My Developer Tools',
loader: () => import('./components/MyToolsPage').then(m => <m.MyToolsPage />),
},
});
const myEntityCard = EntityCardBlueprint.make({
name: 'service-status',
params: {
filter: { kind: 'component' },
loader: () => import('./components/StatusCard').then(m => <m.StatusCard />),
},
});
export default createFrontendPlugin({
pluginId: 'my-tools',
extensions: [myPage, myEntityCard],
});The dual-export pattern
To keep your plug-in working in Red Hat Developer Hub 1.10 while preparing it for 2.1, export both entry points in package.json:
{
"name": "backstage-plugin-my-tools",
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.tsx",
"./package.json": "./package.json"
}
}Developer Hub 1.10 loads ., while Developer Hub 2.1 reads ./alpha.
Backstage dynamic plug-ins: What changed in YAML configuration (platform administrators)
In Red Hat Developer Hub 1.x, configuration files explicitly wired plugin components into layout slots. In the new front-end system, plug-ins provide sensible defaults in code, and administrators use YAML to override or disable those defaults.
Old front-end system (dynamicPlugins.frontend) | New front-end system (app.*) |
|---|---|
Adopter declares UI placement in YAML (mountPoints, importName) | Plug-ins declare defaults in code; adopters override using app.extensions |
Route and sidebar defined under dynamicRoutes | Inferred automatically from page:* extensions |
Overview cards configured under entity.page.overview/cards | Configured under entity-card:* extensions |
Cross-plugin links wired using routeBindings | Declared in app.routes.bindings |
Disabling UI: remove YAML block or set enabled: false | Set <extension-id>: false in app.extensions |
Example: Overriding the placement of an entity card
Old syntax (dynamic-plugins.yaml):
dynamicPlugins:
frontend:
my-company.backstage-plugin-custom:
mountPoints:
- mountPoint: entity.page.overview/cards
importName: CustomOverviewCard
config:
layout:
gridColumn: "1 / span 6"
if:
allOf:
- isKind: componentNew syntax in 2.1 (app-config.yaml or pluginConfig):
app:
extensions:
- entity-card:custom/overview-card:
config:
filter:
kind: component
type: infoExample: Disabling a default card
To hide a built-in card in Red Hat Developer Hub 2.1:
app:
extensions:
- entity-card:catalog/labels: falseConfiguration merging and the app.extensions array
In Red Hat Developer Hub 1.x, front-end layout was split across several files. Default mount points lived in dynamic-plugins.default.yaml, wrappers had their own defaults, and administrators added overrides in dynamic-plugins.yaml and app-config.yaml. Because dynamicPlugins.frontend was a map of objects, Backstage merged these files key by key, which made it hard to track where a given card or route came from.
In Red Hat Developer Hub 2.1, app-config.yaml becomes the sole place to customize the interface. Because plug-ins now define layout defaults directly in TypeScript using extension blueprints, dynamic-plugins.default.yaml drops front-end layout configuration entirely.
First, plug-ins set their own defaults in TypeScript using extension blueprints. Because routes, cards, and default placement are defined in code, dynamic-plugins.default.yaml no longer carries front-end layout configuration.
Furthermore, Backstage treats app.extensions as a YAML array:
app:
extensions:
- entity-card:custom/overview-card:
config:
filter:
kind: componentIn Backstage, configuration files merge objects key by key, but arrays are replaced completely. If you define app.extensions in more than one configuration file, the higher-priority file overwrites the entire array without merging entries by ID.
Because arrays overwrite instead of merging, defining app.extensions in app-config.yaml replaces default plugin configurations cleanly without requiring multi-file reconciliation.
Removal of dynamic plugin wrappers
In Red Hat Developer Hub 1.x, dynamic plugin wrappers were packaged directly inside the core Developer Hub container image in the folder ./dynamic-plugins/dist/. Because these wrappers lived inside the main binary container image, they were tied to the Red Hat Developer Hub release cycle. Patching a plugin bug usually meant waiting for a platform patch release or building a custom container image yourself.
In Red Hat Developer Hub 2.1, all wrappers have been removed from the Developer Hub container image (see RHIDP-10858 and PR #5299). Plug-ins are now published as independent, standalone OCI container images built through Red Hat Konflux pipelines and available on quay.io.
Migration skills for coding agents
To help migrate plugins and configuration files, the Developer Hub team has created 2 assistant skills:
The plugin migration skill (rhdh-plugin-nfs-migration)
Available in the rhdh-skills repository, this skill walks an AI agent through the 6-step plugin migration:
- Inspects
package.json, routes, and components. - Generates extension blueprints (
PageBlueprint,EntityCardBlueprint,EntityContentBlueprint). - Sets up the
./alphaexport map. - Adds a dev app harness and runs TypeScript verification.
Community note: We developed this skill to automate migrating Red Hat’s internal plugin catalog and wanted to share the tooling with the community. While it is an experimental community utility rather than a commercially supported Red Hat feature, it can significantly accelerate your custom plugin refactoring.
The configuration upgrade helper (rhdh-upgrade-helper)
Available in the rhdh-users-skill-pack repository, this skill inspects Helm values, app-config.yaml, and dynamic-plugins.yaml files:
- Flags old
./dynamic-plugins/dist/wrapper paths. - Finds and removes unsupported
!plugin-pathsuffixes. - Converts legacy
dynamicPlugins.frontendmount points intoapp.extensionsblocks.
Testing migrations locally
You can test migrated plug-ins and configuration locally before deploying them to a cluster using RHDH Local.
The main branch of rhdh-local runs Red Hat Developer Hub 1.10 (quay.io/rhdh-community/rhdh:1.10). To test against 2.1 development builds, check out the release-2.1 branch, where compose.yaml uses RHDH_IMAGE=quay.io/rhdh-community/rhdh:next:
git clone https://github.com/redhat-developer/rhdh-local.git
cd rhdh-local
git checkout release-2.1
podman compose up -dOnce started, point rhdh-local at your updated dynamic-plugins.yaml and verify that your plug-ins load properly.
Migration checklist
Before upgrading an environment to Red Hat Developer Hub 2.1:
- Custom front-end plug-ins implement
createFrontendPluginand export using./alpha. - Plugin
package.jsonfiles export both.and./alphaso they work across versions. - Convert
dynamicPlugins.frontendconfiguration blocks toapp.extensionsandapp.routes.bindings. - Remove all
./dynamic-plugins/dist/wrapper paths fromdynamic-plugins.yaml. - Catalog plug-ins use the
ref://reference syntax. APP_CONFIG_app_packageNameandENABLE_STANDARD_MODULE_FEDERATIONare removed from deployment templates.- Catalog index image references are updated to 2.1.
- A test deployment boots cleanly in staging or locally with
rhdh-localon therelease-2.1branch.
What's next
Ready to build your internal developer portal? Explore Red Hat Developer Hub and start a no-cost, 60-day trial. To see how the architecture works, review the upstream Backstage front-end system documentation.