In my previous article, I demonstrated how to manually build and package a Backstage plug-in to make it compatible with the dynamic plug-in system in Red Hat Developer Hub. That approach gives you total control. However, it requires developer expertise and a deep understanding of Backstage and the JavaScript ecosystem. You also need Node.js and Yarn installed locally to manage dependencies, run build commands, and manually push images.
But what if you aren't developing the plug-in? What if you are a platform engineer who simply needs to assemble a set of Backstage plug-ins and keep them updated without maintaining a dozen Git forks or a complex build environment? This article explains how the Dynamic Plug-in Factory can help.
The assembly line approach
The Dynamic Plug-in Factory is an experimental Developer Preview tool from the Red Hat team that shifts the plug-in build process from an imperative approach to a declarative one. Instead of running a sequence of commands, you define build parameters with configuration files.
The plug-in factory reduces the complexity of the build process by encapsulating many of the developer tools within a container image. When you feed it a configuration, it produces ready-to-deploy plug-in artifacts. This ensures plug-ins are built in a clean, reproducible environment every time, regardless of what version of the Backstage dependencies you have in your local development or continuous integration (CI) environments.
Let’s revisit the entity feedback plug-in from the previous article, but this time I’ll demonstrate how to build it using the factory approach.
Prerequisites
Unlike the manual method, you do not need Node.js, Yarn, or the RHDH CLI installed. You only need a container engine like Podman or Docker installed in your environment. To get started with Podman, you can use Podman Desktop, or install the Podman CLI directly using a package manager.
Once you have installed a container engine, pull the Developer Hub 1.8 compatible version of the dynamic plugin factory image. The plug-in built in this article targets Developer Hub v1.8.0 and Backstage v1.42.5 as a result.
podman pull quay.io/rhdh-community/dynamic-plugins-factory:1.8Step 1: Create a configuration
This part is easy. Simply create a folder on your machine. For example, I created a folder named my-plugins. Inside this folder, create a config subfolder. Additionally, create a .gitignore to make sure you don’t commit any secrets by mistake.
mkdir -p my-plugins/config
cd my-plugins
echo ".env" >> .gitignoreStep 1: Define the source
The factory operates on one repository at a time. You define this upstream source in a config/source.json file. The repo-ref is pointing to a specific commit that bumps the Entity Feedback plug-in version to support Backstage 1.42.3 close to Developer Hub 1.8’s Backstage version of 1.42.5.
{
"repo": "https://github.com/backstage/community-plugins",
"repo-ref": "2b60b5959e821b216f9c8fb224caf9fc8d51f075"
}Pro tip: The source.json only accepts a single repo. You can build multiple workspaces from a single repository. To build plug-ins from different repositories (e.g., the Backstage Community plug-ins repository and a private internal repository), you will need to create separate config folders for each upstream and run the factory for each config.
Step 2: Specify the target plug-ins
The community repository contains dozens of plug-ins. Create a plugins-list.yaml to specify exactly which packages should be built and published as dynamic plug-ins:
plugins/entity-feedback:
plugins/entity-feedback-backend: --embed-package @backstage/plugin-notifications-node --embed-package @backstage/plugin-notifications-commonThe paths in this file specifically reference the plug-in paths for the targeted plug-in workspace. In this case, the workspace path is workspaces/entity-feedback and the subfolders to build plug-ins are those listed in the plugins-list.yaml.
You can add more arguments to each entry in this file. For example, certain plug-ins specify other plug-ins as dependencies. Use the --embed-package argument to ensure these dependencies are met.
In the case of the Entity Feedback backend, embed these two packages (other plug-ins):
- @backstage/plugin-notifications-node
- @backstage/plugin-notifications-common
Failure to specify these embedded packages would result in MODULE_NOT_FOUND errors when Developer Hub attempts to load the entity-feedback-backend plug-in.
If you notice MODULE_NOT_FOUND errors when loading a dynamic plug-in you’ve packaged, that's a strong indication you need to embed the specified module or the pre-installed plug-in in Developer Hub.
Note
Node.js uses the term “module” to refer to libraries. In Backstage, plug-ins are distributed as Node.js modules. Broadly speaking, “packages” is a runtime agnostic term and is therefore used by the factory.
Step 3: Configure the registry (.env)
In the manual method, you had to run podman login and podman push for every plug-in. The factory automates this process.
Create a .env file in the config directory. This file tells the factory where to push the resulting container images.
# Registry Configuration
REGISTRY_URL=quay.io
REGISTRY_NAMESPACE=your-namespace # usually this is your username or org name
REGISTRY_USERNAME=your-quay-or-robot-username
REGISTRY_PASSWORD=your-quay-or-robot-passwordNote, I’m using a robot account with specific write permissions instead of passing my Quay username and password directly to the factory container. Other container registries support similar capabilities. Using a robot account is optional, but recommended when build systems will access Quay.io on your behalf.
Configure repositories and a robot account
To configure a robot account, first create the image repositories your factory will push the plug-ins to. In this case, those are named backstage-community-plugin-entity-feedback and backstage-community-plugin-entity-feedback-backend.
Figure 1 shows an example of creating an empty repository on Quay.io with the correct name.

Next, go to your Account Settings and select Robot Accounts. Create a new robot account, or reuse an existing one, then assign it write permissions for your two new repositories. This is shown in Figure 2.

Lastly, obtain the robot account username and password by clicking on it in the Robot Accounts listing, as shown in Figure 3.

Step 4: Run the factory
This is where automation takes over. No need to run yarn install or tsc. Simply run the factory container, mounting the config directory and specifying the target workspace.
Note that we are adding the --push-images flag to tell the factory to push the images to the container registry specified in the .env configuration.
podman run --rm -it \
--device /dev/fuse \
-v ./config:/config \
quay.io/rhdh-community/dynamic-plugins-factory:1.8 \
--push-images \
--workspace-path=workspaces/entity-feedbackThe container will:
- Clone the repository defined in source.json.
- Install all necessary dependencies in an isolated environment.
- Build the plug-ins listed in plugins-list.yaml.
- Package them and automatically push them to Quay.io.
Once the build is complete, make sure that the new container image repositories are public to avoid the need to configure pull secrets to follow this article. Figure 4 shows where the repository visibility setting is located in Quay.io.

Step 5: Add the plug-ins to Developer Hub
Once the factory has successfully pushed your images to Quay.io, the process for adding them to your Red Hat Developer Hub instance is identical to the manual method.
You will need to update your dynamic-plugins.yaml configuration to point to the new OCI images you just created.
Note
For a detailed explanation of the pluginConfig and mountPoints for this specific plug-in, refer to the "Add the plug-ins to Developer Hub" section in my previous article and the official documentation for frontend plug-ins.
plugins:
- package: oci://quay.io/evanshortiss/backstage-community-plugin-entity-feedback:0.9.0!backstage-community-plugin-entity-feedback
disabled: false
pluginConfig:
dynamicPlugins:
frontend:
backstage-community.plugin-entity-feedback:
entityTabs:
- mountPoint: entity.page.feedback
path: /feedback
title: Feedback
mountPoints:
- config:
layout:
gridColumn: 1 / -1
importName: StarredRatingButtons
mountPoint: entity.page.feedback/cards
- config:
layout:
gridColumn: 1 / -1
importName: EntityFeedbackResponseContent
mountPoint: entity.page.feedback/cards
- package: oci://quay.io/evanshortiss/backstage-community-plugin-entity-feedback-backend:0.11.0!backstage-community-plugin-entity-feedback-backend
disabled: falseOnce the plug-ins are added to your configuration, restart Developer Hub and confirm they’ve been installed by viewing the container logs and viewing the Installed packages section in the Admin > Extensions view, as shown in Figure 5.

Customizing patches, overlays, and forks
The factory allows you to apply patch files and overlays to the source code before building. If you need to change a single CSS color or a hardcoded URL, a patch file is a quick mechanism that avoids the need to maintain a permanent fork of the repository. You can use overlays to replace entire files instead of specific parts of a file. However, if you are making significant changes, such as rewriting logic or adding features, writing patches, or overlays could become tedious. In those cases, maintaining a fork might be a better alternative.
Wrap up
If you are a plug-in developer, you may prefer your own process. If you are a platform engineer tasked with curating a catalog of many different open-source plug-ins for your internal developer portal, the Dynamic Plugin Factory offers a cleaner, more scalable way to manage your supply chain.
Visit the Dynamic Plug-in Factory on GitHub. We welcome your contributions!