Dynamic Plugins in Red Hat Developer Hub

Table of Contents

Red Hat Developer Hub and Dynamic Plugins

Red Hat Developer Hub (RHDH) is the enterprise version of Backstage, designed to centralize services, documentation, templates, and toolchains in a single interface for modern organizations, improving collaboration and accelerating innovation.

RHDH offers a ready-to-use portal with enterprise support, role-based access control (RBAC), audit logging, and multi-cloud scalability.

The distinctive feature of RHDH is Dynamic Plugins: the ability to extend the platform at runtime. Instead of requiring complete rebuild and deployment cycles of the core application, teams can add new functionality by modifying YAML configuration files, promoting modularity, customization, reduced maintenance overhead, and faster iteration.

It’s important to note that while Dynamic Plugins eliminate the need to rebuild the core application, their installation typically requires an RHDH application restart. In containerized environments like Red Hat OpenShift, this restart is managed through rolling updates, minimizing user-perceived disruption.

Technical Pattern vs Business Pattern: An Important Distinction

Before diving into the technical details, it’s crucial to understand that RHDH Dynamic Plugins are not microfrontends in the traditional business sense, despite sharing some technical implementation patterns.

🔍 Key Insight: RHDH Dynamic Plugins implement a “Plugin Architecture with Module Federation” pattern. While they use microfrontend-like technical solutions (dynamic loading, Module Federation), they are platform extensions, not independent business applications. This distinction is crucial for understanding their proper enterprise application and organizational impact.

What They Actually Are:

  • Plugin Architecture Pattern: Runtime extensible platform
  • Dynamic Module System: Hot-pluggable extensions
  • Platform Extensions: Tools that extend developer portal capabilities
  • Runtime Extension System: Modular composition within a unified platform

What They Are NOT:

  • Business Domain Applications: They don’t represent separate business domains
  • Independent Applications: They extend a single platform rather than compose separate apps
  • Organizational Scaling Solution: They solve technical extensibility, not team independence
  • True Microfrontends: They lack the business domain separation that defines microfrontends

Why the Confusion?

The confusion arises because both patterns use similar technical approaches:

  • âś… Dynamic loading at runtime
  • âś… Module Federation (Webpack 5)
  • âś… Independent development and deployment
  • âś… Technology diversity within modules
  • âś… Team autonomy in development

However, the business context and architectural intent are fundamentally different:

Aspect True Microfrontends RHDH Dynamic Plugins
Purpose Business domain separation Platform extensibility
Scope Complete user applications Developer tool features
Teams Business domain ownership Platform/tool specialization
Runtime Multiple independent apps Single platform with extensions
State Isolated application state Shared platform context
URL Structure Different domains/subdomains Same domain, different routes
Business Goal Domain autonomy Tool integration

Static vs Dynamic Plugins Comparison

Feature Static Plugins Dynamic Plugins
Integration Integrated in application core Loaded at runtime, separate from core
Flexibility Require core modifications Add/update functionality without core changes
Development Speed Slower, require complete rebuild Faster, deploy new features independently
Customization Limited to predefined options Easy customization
Maintenance More complex Improved by modular architecture
Resource Usage All features loaded at startup Only necessary plugins loaded dynamically
Innovation Slower experimentation Rapid experimentation with new plugins

The RHDH Ecosystem: Red Hat Integration

Dynamic Plugins integrate deeply with the entire Red Hat ecosystem: OpenShift for orchestration, Ansible for automation, GitOps for deployment, Quay for registries, and Red Hat AI for artificial intelligence capabilities. This interoperability optimizes workflows and accelerates innovation, leveraging existing operational models like ConfigMaps and Helm charts for plugin deployment.

There are three types of RHDH dynamic plugins:

  • Frontend Plugin: Static user interface written in React and TypeScript that integrates with internal or external APIs
  • Backend Plugin: Backend API written in TypeScript that exposes APIs to frontend plugins
  • Full-Stack Plugin: Combination of frontend and backend TypeScript code for specific functionality
graph LR subgraph TYPES["Plugin Types"] FE_PLUGIN[Frontend Plugin
React + TypeScript
UI Components] BE_PLUGIN[Backend Plugin
TypeScript APIs
Server Logic] FS_PLUGIN[Full-Stack Plugin
Frontend + Backend
Complete Features] end subgraph DEPLOYMENT["Deployment Options"] STATIC[Static Plugin
Core Integration
Requires Rebuild] DYNAMIC[Dynamic Plugin
Runtime Loading
No Rebuild] end FE_PLUGIN --> STATIC FE_PLUGIN --> DYNAMIC BE_PLUGIN --> STATIC BE_PLUGIN --> DYNAMIC FS_PLUGIN --> STATIC FS_PLUGIN --> DYNAMIC style DYNAMIC fill:#e8f5e8 style STATIC fill:#fff3e0

Each type can be deployed as Static Plugin (integrated in RHDH core, requires rebuild) or Dynamic Plugin (loaded at runtime without recompilation), with Dynamic Plugins representing the main extensibility architecture.

Architecture: Two Parallel and Independent Systems

Dynamic Plugins in RHDH operate through two parallel systems that require different technical strategies, reflecting the fundamentally different nature of the problems that backend and frontend must address.

graph TB subgraph " " FE[Frontend Plugins - Scalprum] BE[Backend Plugins - Dynamic PM] CONFIG[Shared YAML - Configuration] FE <-->|API/HTTP| BE FE -.->|Configuration| CONFIG BE -.->|Configuration| CONFIG end style FE fill:#e1f5fe style BE fill:#f3e5f5 style CONFIG fill:#fff3e0

Backend Plugin System

Backend plugins extend server-side APIs and services through the Dynamic Plugin Manager. This system automatically discovers plugins in the configured directory and integrates them into the runtime using standard Node.js patterns: dynamic imports, dependency injection, and service registry. Dynamic loading in server environments leverages native runtime capabilities without particular architectural complexities.

Frontend Plugin System

Frontend plugins require a completely different architectural approach, managed by the Scalprum system. These plugins must integrate into an existing React application, operating in a browser environment with specific constraints: bundling, shared namespace, module resolution, and distributed state management.

Although the two systems are independent, they communicate indirectly through coordinated configuration via shared YAML, proxy routing for API access, metadata sharing, and lifecycle coordination for simultaneous deployment of full-stack components.

Dynamic Plugin Sequence in RHDH

This sequence diagram details the step-by-step interactions involved in loading and activating a dynamic plugin in Red Hat Developer Hub (RHDH):

sequenceDiagram participant User participant ArtifactRegistry as Artifact Registry / HTTP / NPM / OCI participant InstallScript as install-dynamic-plugins.py participant Backend participant PluginLoader participant API participant Frontend participant Scalprum participant Registry participant AppComponent User->>ArtifactRegistry: Upload dynamic plugin (.tgz) InstallScript->>ArtifactRegistry: Fetch plugin package InstallScript->>Backend: Place plugin in dynamic-plugins-root Backend->>PluginLoader: Load all plugins and serve static assets from dynamic-plugins-root PluginLoader->>Backend: Register plugin and update metadata Backend->>API: Expose loaded plugin metadata/API Frontend->>API: Request list/metadata of dynamic plugins API-->>Frontend: Return available plugin info Frontend->>Scalprum: Request loading of plugin JS bundle Scalprum-->>Frontend: Load bundle via Module Federation Frontend->>Registry: Update ComponentRegistry with new extensions Registry->>AppComponent: Propagate new extensions via DynamicRootContext AppComponent->>User: Show new routes/menus/UI components
  • The user uploads a dynamic plugin package (.tgz) to a location accessible by the install-dynamic-plugins.py script, such as an HTTP server, npm registry, or OCI artifact registry. The plugin location is identified using the appropriate URL qualifier (http/https, npm package name, or oci).

    Note: There is currently no way to publish dynamic plugins directly to RHDH through its UI or backend.

  • The backend loads all dynamic plugins from the dynamic-plugins-root directory and also serves plugin static assets from that location, using the PluginLoader to register them and expose their metadata and APIs.

  • The backend exposes the loaded plugin’s metadata and APIs through its API endpoints.

  • The frontend requests the list and metadata of available dynamic plugins from the backend API.

  • The API responds with information about the available plugins.

  • The frontend asks Scalprum to load the plugin’s JavaScript bundle using Module Federation.

  • Once loaded, the frontend updates the central ComponentRegistry with the new extensions provided by the plugin.

  • These extensions are propagated to application components via the DynamicRootContext.

  • Finally, the application components render new routes, menus, or UI elements provided by the dynamic plugin, making them available to the user.

Module Federation and Plugin Architecture

The Frontend Plugin Problem

To understand the need for Scalprum, we must understand why dynamic loading in the browser is fundamentally different from the server. In the backend, Node.js offers a simple native system:

// Backend: simple and direct
const plugin = await import('./my-backend-plugin');
plugin.register(backend);

In the browser, this simplicity disappears. React components don’t exist as separate files after bundling. Webpack transforms everything into optimized bundles where dependencies are intertwined. How do you dynamically load something that has been “dissolved” in a monolithic bundle?

The browser also shares a single global namespace. In enterprise contexts, this creates complex scenarios: plugin A might require React 18.1, plugin B React 18.2, while the host application uses React 17.0.

Scalprum

The adoption of Scalprum as the framework for frontend Dynamic Plugins is based on Module Federation, a Webpack 5 technology that solves these concrete problems. Module Federation transforms how JavaScript applications can be composed:

  • Remote Containers: Each plugin is an autonomous container that exposes specific modules
  • Shared Scope: Dependencies are shared through a global scope managed by Webpack
  • Async Boundaries: Asynchronous boundaries enable lazy loading of plugins
  • Runtime Composition: Composition happens completely at runtime, not build time

Module Federation Architecture:

graph TB subgraph HOST["🏠 Host Application"] SHARE[Webpack Share Scope
Dependencies & Version Control] MOUNT[Mount Points] end subgraph REMOTE_A["📦 Remote Container A"] MODULES_A[Exposed Modules] ENTRY_A[remoteEntry.js] end subgraph REMOTE_B["📦 Remote Container B"] MODULES_B[Exposed Modules] ENTRY_B[remoteEntry.js] end BUILD_A[Build Time Bundle A] --> ENTRY_A BUILD_B[Build Time Bundle B] --> ENTRY_B MOUNT <--> MODULES_A MOUNT <--> MODULES_B SHARE -.-> MODULES_A SHARE -.-> MODULES_B ENTRY_A -.->|Runtime Loading| MODULES_A ENTRY_B -.->|Runtime Loading| MODULES_B style HOST fill:#e8f5e8 style REMOTE_A fill:#fff3e0 style REMOTE_B fill:#e3f2fd style BUILD_A fill:#f5f5f5 style BUILD_B fill:#f5f5f5

Plugin Architecture with Microfrontend-like Characteristics

Frontend dynamic plugins implement a plugin architecture pattern that shares some technical characteristics with microfrontends but serves a different purpose:

Important Distinction: While these plugins use microfrontend-like technical patterns (Module Federation, dynamic loading), they are not microfrontends in the business sense. They are:

  • Technical extensions of a unified platform (RHDH)
  • Feature plugins rather than business domain applications
  • Runtime extensions that extend platform capabilities
  • Tool integrations that enhance developer experience

Key Differences from True Microfrontends:

  • Shared Runtime: All plugins run within the same Backstage context
  • Unified Deployment: Single RHDH instance, not separate applications
  • Platform Extension: Extend developer tools, not business domains
  • Shared State Management: Common APIs and contexts across plugins
  • Same URL Space: All plugins operate under the same domain
  • Coordinated UX: Consistent user experience across all plugins

Technical Characteristics (Similar to Microfrontends):

  • Independent Development: Each plugin is a separate project with its own repository, dependencies, and build process
  • Independent Deployment: Plugins are deployed as separate artifacts without requiring new RHDH releases
  • Technical Isolation: Each plugin operates in its own isolated context with error boundaries that confine problems
  • Runtime Composition: Plugins are discovered and loaded dynamically in the browser

Dependency Management and Mount Points

Intelligent Dependency Management

Note: This feature is still in an early stage, but it’s worth mentioning.

Scalprum uses a deterministic strategy to manage common library versions between host application and plugins:

// Module Federation configuration in RHDH
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'backstageHost',
      filename: 'remoteEntry.js',
      shareStrategy: 'loaded-first',
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^18.0.0',
          eager: true,
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^18.0.0',
          eager: true,
        },
        '@backstage/core-plugin-api': {
          singleton: true,
          eager: true,
        },
      },
    }),
  ],
};

With singleton: true, Module Federation forces the use of a single dependency instance. The “loaded-first” strategy uses the first loaded version. If a plugin requires an incompatible version, Module Federation will emit a warning but continue with the existing version, ensuring stability.

Mount Points: Structured UI Integration

Mount Points are predefined extension points in the user interface where Module Federation plugins can integrate. This RHDH abstraction solves one of the most complex problems of plugin systems: where and how external components can integrate into existing UI.

Mount Points operate at different granularity levels:

  • Page-level: entity.page.* to add entire tabs to Catalog entity pages
  • Component-level: *.cards to insert specific widgets in designated areas
  • Context-level: *.context to provide React Context and shared state

Each Mount Point supports advanced configurations for conditional rendering:

mountPoints:
  - mountPoint: entity.page.overview/cards
    config:
      if:
        allOf:
          - isKind: component
          - hasAnnotation: 'example.com/my-annotation'
      layout:
        gridColumn: '1 / -1'
        minHeight: 200

This architecture provides:

  • Optimized Code Sharing: Automatic deduplication of shared dependencies
  • Controlled Isolation: Controlled isolation where plugins share selected dependencies while maintaining clear boundaries
  • Deterministic Versioning: Singleton strategy that eliminates version conflicts
  • Build Independence: Each plugin built and deployed independently

Impact on Development Experience

The adoption of Scalprum and Module Federation radically transforms how client organizations can extend RHDH. Teams develop, test, and distribute their own frontend plugins without depending on Red Hat release cycles or forking the core product.

The isolation provided by Module Federation means new UI features carry limited risk to overall stability, encouraging experimentation and innovation.

Managing custom plugins in enterprise environments leverages Kubernetes infrastructure. Organizations deploy their own remote containers using standard operational models: ConfigMaps for configuration and Helm charts for deployment.

The plugin architecture supports platform extensibility, enabling specialized teams to develop platform extensions without conflicts. This promotes a plugin ecosystem model rather than a business domain separation model. Communication between plugins happens through well-defined patterns that maintain decoupling. The end-user experience remains smooth and integrated, with performance optimized by asynchronous loading and code deduplication.