Red Hat Developer Hub 1.9

Develop and deploy dynamic plugins in Red Hat Developer Hub

RHDH dynamic plugins: From development to deployment

Red Hat Customer Content Services

Abstract

The Red Hat Developer Hub (RHDH) application offers a unified platform with various plugins. Using the plugin ecosystem within the Developer Hub application, you can access your development infrastructure and software development tools.

1. Developing and deploying dynamic plugins in Red Hat Developer Hub

1.1. Overview of dynamic plugins

1.1.1. Dynamic Plugins

Red Hat Developer Hub implements a dynamic plugin system. You can install, configure, and load plugins at runtime without changing or rebuilding the application. You only need a restart. You can load these plugins from NPM, tarballs, or OCI compliant container images.

With dynamic plugins, instead of modifying the Backstage application itself, you create a dynamic-plugins.yaml file to specify the plugins that Red Hat Developer Hub will install and enable at startup. For example, the following configuration loads a plugin named plugin-name, which is stored in a Quay.io container image at quay.io/account-name/image-name:

dynamic-plugins.yaml fragment

plugins:
  - package: oci://quay.io/account-name/image-name:tag!plugin-name
    disabled: false
    pluginConfig: {}

1.2. Prepare your developement environment

Before creating or converting plugins for Red Hat Developer Hub (product-very-short), you must establish a specific local development toolchain. This toolchain allows you to write standard Backstage code, convert it into a dynamic format, and package it for deployment without rebuilding the core RHDH platform.

1.2.1. Required skills and languages

To successfully develop dynamic plugins, you should possess the following technical proficiencies:

JavaScript and TypeScript
The core languages used for Backstage frontend and backend development.
React
Required for building frontend plugin components.
Node.js ecosystem
Familiarity with package management (NPM/Yarn) and module handling.

1.2.2. The development toolchain

The following tools are essential for initializing, building, and packaging your plugins:

Node.js (via NVM)

Node.js is the engine that runs JavaScript on your computer.

Backstage requires specific Active LTS (Long Term Support) versions (v18 or v20) to function correctly. Using Node Version Manager (NVM) allows you to switch between these versions easily, ensuring compatibility with the RHDH backend system.

Yarn (Classic)

Yarn is a package manager that handles all the libraries (dependencies) that your application needs.

The Backstage project structure is optimized for Yarn (specifically Yarn Classic 1.x) to manage workspaces and dependencies efficiently.

Containerization tools (Docker or Podman)

These tools used to run containers and package applications.

  • Packaging: Dynamic plugins are recommended to be distributed as OCI images. You use Docker or Podman to package your derived plugin assets into an image that can be pushed to a registry (for example, Quay.io) and sideloaded into RHDH.
  • TechDocs: Docker is used locally to run containers that generate technical documentation.
RHDH plugin tools

These specialized tools facilitate the conversion of standard plugins into the dynamic architecture required by RHDH.

  • RHDH Plugin Factory and rhdh-cli: These tools assist in initializing new plugins or converting existing standard Backstage plugins into the RHDH dynamic plugin format. They help structure the code to support dynamic loading.
  • Janus IDP CLI (@janus-idp/cli): This command-line tool is critical for the export process. It allows you to run commands like export-dynamic-plugin, which repackages your code into a derived package containing the necessary configuration (like Scalprum for frontend) and dependency handling (bundling private dependencies vs. sharing platform dependencies)

1.3. Developing a new plugin

1.4. Converting a custom plugin into a dynamic plugin

1.4.1. Example of installing a custom plugin in Red Hat Developer Hub

This example demonstrates how to package and install dynamic plugins using the Backstage Entity Feedback community plugin that is not included in Red Hat Developer Hub pre-installed dynamic plugins.

Limitations

  • You need to ensure that your custom plugin is built with a compatible version of Backstage. In Developer Hub, click Settings. Your custom plugin must be compatible with the Backstage Version (or the closest previous version) that is displayed in the Metadata section of Red Hat Developer Hub.

    For example, if you view the history of the backstage.json file for the Entity Feedback plugin, the 1fc87de commit is closest previous version to Backstage version of 1.39.1.

    Figure 1. backstage.json file history in Github

    custom limitations

Prerequisites

  • Your local environment meets the following requirements:
  • Node.js: Version 22.x
  • Yarn: Version 4.x
  • git CLI
  • jq CLI: Command-line JSON processor
  • OpenShift CLI (oc): The client for interacting with your OpenShift cluster.
  • Container runtime: Either podman or docker is required for packaging the plugin into an OCI image and logging into registries.
  • Container registry access: Access to an OCI-compliant container registry (such as the internal OpenShift registry or a public registry like Quay.io).

Procedure

  1. Clone the source code for the Entity Feedback plugin, as follows:

    $ git clone https://github.com/backstage/community-plugins.git
    $ cd community-plugins
  2. Prepare your environment to build the plugin by enabling Yarn for your Node.js installation, as follows:

    $ corepack enable yarn
  3. Install the dependencies, compile the code, and build the plugins, as follows:

    $ cd workspaces/entity-feedback
    $ yarn install
    $ yarn tsc
    $ yarn build:all
    Note

    After this step, with upstream Backstage, you publish the built plugins to a NPM or NPM-compatible registry. In this example, as you are building this plugin to support it being loaded dynamically by Red Hat Developer Hub, you can skip the npm publish step that publishes the plugin to a NPM registry. Instead, you can package the plugin for dynamic loading and publish it as a container image on Quay.io or your preferred container registry.

  4. Prepare the Entity Feedback frontend plugin by using the Red Hat Developer Hub CLI. The following command uses the plugin files in the dist folder that was generated by the yarn build:all command, and creates a new dist-scalprum folder that contains the necessary configuration and source files to enable dynamic loading:

    $ cd plugins/entity-feedback
    $ npx @red-hat-developer-hub/cli@latest plugin export

    When this command packages a frontend plugin, it uses a default Scalprum configuration if one is not found. The Scalprum configuration is used to specify the plugin entry point and exports, and then to build a dist-scalprum folder that contains the dynamic plugin. The default Scalprum configuration is shown below, however a scalprum key can be added to the package.json file used by your plugin to set custom values, if necessary:

    {
      "name": "backstage-community.plugin-entity-feedback",
      "exposedModules": {
        "PluginRoot": "./src/index.ts"
      }
    }

    The following plugin-manifest.json file, which Red Hat Developer Hub uses to load the plugin, is located in the dist-dynamic/dist-scalprum folder:

    {
      "name": "backstage-community.plugin-entity-feedback",
      "version": "0.6.0",
      "extensions": [],
      "registrationMethod": "callback",
      "baseURL": "auto",
      "loadScripts": [
        "backstage-community.plugin-entity-feedback.fd691533c03cb52c30ac.js"
      ],
      "buildHash": "fd691533c03cb52c30acbb5a80197c9d"
    }
  5. Package the plugin into a container image and publish it to Quay.io or your preferred container registry:

    $ export QUAY_USER=replace-with-your-username
    $ export PLUGIN_NAME=entity-feedback-plugin
    $ export VERSION=$(cat package.json | jq .version -r)
    
    $ npx @red-hat-developer-hub/cli@latest plugin package \
      --tag quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION
    
    $ podman login quay.io
    $ podman push quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION
  6. Repeat the same steps for the backend plugin. Scalprum is not required for backend plugins, and a dist-dynamic folder is generated instead of a dist-scalprum folder:

    $ cd ../entity-feedback-backend/
    $ npx @red-hat-developer-hub/cli@latest plugin export
    
    $ export QUAY_USER=replace-with-your-username
    $ export PLUGIN_NAME=entity-feedback-plugin-backend
    $ export VERSION=$(cat package.json | jq .version -r)
    
    $ npx @red-hat-developer-hub/cli@latest plugin package \
      --tag quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION
    
    $ podman push quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION

    Those commands result in two container images being published to your container registry.

    Figure 2. Container images published to Quay.io

    custom container images

1.5. Packaging and publishing artifacts

  • Open Container Initiative (OCI) image (recommended)
  • TGZ file
  • JavaScript package

    Important

    Exported dynamic plugin packages must only be published to private NPM registries.

1.5.1. Creating an OCI image with dynamic packages

Prerequisites

  • You have installed podman or docker.

Procedure

  1. Navigate to the plugin’s root directory (not the dist-dynamic directory).
  2. Run the following command to package the plugin into an OCI image:

    Example command to package an exported custom plugin

    $ npx @red-hat-developer-hub/cli@latest plugin package --tag quay.io/example/image:v0.0.1

    In the previous command, the --tag argument specifies the image name and tag.

  3. Run one of the following commands to push the image to a registry:

    Example command to push an image to a registry using podman

    $ podman push quay.io/example/image:v0.0.1

    Example command to push an image to a registry using docker

    $ docker push quay.io/example/image:v0.0.1

    The output of the package-dynamic-plugins command provides the plugin’s path for use in the dynamic-plugin-config.yaml file.

1.5.2. Creating a TGZ file with dynamic packages

Prerequisites

assembly-developing-and-deploying-plugins

Procedure

  1. Navigate to the dist-dynamic directory.
  2. Run the following command to create a tgz archive:

    Example command to create a tgz archive

    $ npm pack

    You can obtain the integrity hash from the output of the npm pack command by using the --json flag as follows:

    Example command to obtain the integrity hash of a tgz archive

    $ npm pack --json | head -n 10

  3. Host the archive on a web server accessible to your RHDH instance, and reference its URL in the dynamic-plugin-config.yaml file as follows:

    Example dynamic-plugin-config.yaml file

    plugins:
      - package: https://example.com/backstage-plugin-myplugin-1.0.0.tgz
        integrity: sha512-<hash>

  4. Run the following command to package the plugins:

    Example command to package a dynamic plugin

    $ npm pack --pack-destination ~/test/dynamic-plugins-root/

    Tip

    To create a plugin registry using HTTP server on OpenShift Container Platform, run the following commands:

    Example commands to build and deploy an HTTP server in OpenShift Container Platform

    $ oc project my-rhdh-project
    $ oc new-build httpd --name=plugin-registry --binary
    $ oc start-build plugin-registry --from-dir=dynamic-plugins-root --wait
    $ oc new-app --image-stream=plugin-registry

  5. Configure your RHDH to use plugins from the HTTP server by editing the dynamic-plugin-config.yaml file:

    Example configuration to use packaged plugins in RHDH

    plugins:
      - package: http://plugin-registry:8080/backstage-plugin-myplugin-1.9.6.tgz

1.5.3. Creating a JavaScript package with dynamic packages

Warning

The derived dynamic plugin JavaScript packages must not be published to the public NPM registry. If you must publish to the NPM registry, use a private registry.

Procedure

  1. Navigate to the dist-dynamic directory.
  2. Run the following command to publish the package to your private NPM registry:

    Example command to publish a plugin package to an NPM registry

    $ npm publish --registry <npm_registry_url>

    Tip

    You can add the following to your package.json file before running the export command:

    Example package.json file

    {
      "publishConfig": {
        "registry": "<npm_registry_url>"
      }
    }

    If you modify publishConfig after exporting the dynamic plugin, re-run the plugin export command to ensure the correct configuration is included.

1.6. Verifying plugins locally

1.7. Deployment configurations

1.8. Maintain and scale plugins to ensure long-term stability

Legal Notice

Copyright © 2026 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.