# @backstage/backend-dynamic-feature-service

> Backstage dynamic feature service

Latest version **0.8.7** (published 2026-09-15) · Apache-2.0 license · 0 weekly downloads

## Install

```sh
npm install @backstage/backend-dynamic-feature-service
pnpm add @backstage/backend-dynamic-feature-service
yarn add @backstage/backend-dynamic-feature-service
bun add @backstage/backend-dynamic-feature-service
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score; popular repo.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.8.7 |
| Published | 2026-09-15 |
| First published | 2024-01-16 |
| Weekly downloads | 0 |
| License | Apache-2.0 |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 27 |
| Unpacked size | 290 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 34399 |
| Maintainers | patriko, freben, marcuseide |
| Keywords | backstage |

## Links

- npm: https://www.npmjs.com/package/@backstage/backend-dynamic-feature-service
- Repository: https://github.com/backstage/backstage
- Homepage: https://backstage.io
- npm.io page: https://npm.io/package/@backstage/backend-dynamic-feature-service

## Dependencies (27)

- [lodash](https://npm.io/package/lodash.md) ^4.17.21
- [express](https://npm.io/package/express.md) ^4.22.0
- [winston](https://npm.io/package/winston.md) ^3.2.1
- [chokidar](https://npm.io/package/chokidar.md) ^3.5.3
- [fs-extra](https://npm.io/package/fs-extra.md) ^11.2.0
- [@backstage/types](https://npm.io/package/@backstage/types.md) ^1.2.2
- [@backstage/config](https://npm.io/package/@backstage/config.md) ^1.3.9
- [@backstage/errors](https://npm.io/package/@backstage/errors.md) ^1.3.1
- [@backstage/cli-node](https://npm.io/package/@backstage/cli-node.md) ^0.3.4
- [@backstage/cli-common](https://npm.io/package/@backstage/cli-common.md) ^0.3.1
- [@manypkg/get-packages](https://npm.io/package/@manypkg/get-packages.md) ^1.1.3
- [@module-federation/sdk](https://npm.io/package/@module-federation/sdk.md) ~2.8.2
- [express-promise-router](https://npm.io/package/express-promise-router.md) ^4.1.0
- [@backstage/config-loader](https://npm.io/package/@backstage/config-loader.md) ^1.11.3
- [@backstage/plugin-app-node](https://npm.io/package/@backstage/plugin-app-node.md) ^0.1.49
- [@backstage/backend-defaults](https://npm.io/package/@backstage/backend-defaults.md) ^0.18.0
- [@backstage/plugin-auth-node](https://npm.io/package/@backstage/plugin-auth-node.md) ^0.7.6
- [@backstage/backend-plugin-api](https://npm.io/package/@backstage/backend-plugin-api.md) ^1.10.1
- [@backstage/plugin-events-node](https://npm.io/package/@backstage/plugin-events-node.md) ^0.4.26
- [@backstage/plugin-search-common](https://npm.io/package/@backstage/plugin-search-common.md) ^1.2.25
- [@backstage/backend-openapi-utils](https://npm.io/package/@backstage/backend-openapi-utils.md) ^0.7.2
- [@backstage/plugin-events-backend](https://npm.io/package/@backstage/plugin-events-backend.md) ^0.6.6
- [@backstage/plugin-catalog-backend](https://npm.io/package/@backstage/plugin-catalog-backend.md) ^4.0.0
- [@backstage/plugin-permission-node](https://npm.io/package/@backstage/plugin-permission-node.md) ^0.11.4
- [@backstage/plugin-scaffolder-node](https://npm.io/package/@backstage/plugin-scaffolder-node.md) ^0.13.7
- [@backstage/plugin-permission-common](https://npm.io/package/@backstage/plugin-permission-common.md) ^0.9.11
- [@backstage/plugin-search-backend-node](https://npm.io/package/@backstage/plugin-search-backend-node.md) ^1.4.8

## Recent versions

- 0.8.7 (latest) — 2026-09-15
- 0.0.0-nightly-20260919022921 (nightly) — 2026-09-19
- 0.8.7-next.2 (next) — 2026-09-08
- 0.0.0-nightly-20260918023200 — 2026-09-18
- 0.0.0-nightly-20260917023108 — 2026-09-17
- 0.0.0-nightly-20260916022921 — 2026-09-16
- 0.0.0-nightly-20260909022715 — 2026-09-09
- 0.0.0-nightly-20260904022803 — 2026-09-04
- 0.0.0-nightly-20260902024315 — 2026-09-02
- 0.8.7-next.1 — 2026-09-01
- 0.0.0-nightly-20260829022506 — 2026-08-29
- 0.0.0-nightly-20260828045505 — 2026-08-28
- 0.0.0-nightly-20260827035829 — 2026-08-27
- 0.0.0-nightly-20260826023806 — 2026-08-26
- 0.8.7-next.0 — 2026-08-25
- … 975 more at https://npm.io/package/@backstage/backend-dynamic-feature-service/versions

## README

# @backstage/backend-dynamic-feature-service

This package provides experimental support for **dynamic backend features (plugins and modules)** in Backstage, allowing you to load plugins at runtime from a separate directory without including them in your main application's package.json.

## Purpose

This enables:

- **Plugin distribution**: Distributing plugins as standalone artifacts
- **Runtime flexibility**: Adding or updating plugins without rebuilding the entire application
- **Isolation**: Keeping plugin-specific dependencies separate from core application dependencies
- **Modular deployments**: Different environments can load different sets of plugins

**Important:** This service handles loading only - packaging and distribution are separate concerns with multiple approaches available (see Packaging Approaches section).

## Related Packages

- **`@backstage/frontend-dynamic-feature-loader`**: Companion package for loading dynamic frontend plugins. This is **only** supported in the New Frontend System. For more details, checkout the [README](../frontend-dynamic-feature-loader/README.md).

## How it works

### Core Architecture

The service consists of several key components:

1. **Plugin Scanner**: Scans a configured directory (`dynamicPlugins.rootDirectory`) for plugin packages
2. **Module Loader**: Loads plugin modules
3. **Feature Loader**: Integrates loaded plugins into the Backstage backend system
4. **Frontend Asset Server**: Serves frontend plugin assets (JavaScript, CSS, manifests) over HTTP for dynamic frontend plugins

### Loading Process

1. **Discovery**: The scanner identifies valid plugin packages in the dynamic plugins root directory
2. **Validation**: Each package is validated for required fields (`main`, `backstage.role`)
3. **Loading**: The module loader loads the plugin code
4. **Integration**: Plugins are registered as Backstage features and made available to the backend

## Usage

### Basic Setup

Add the service to your backend application:

1. **Install the package**:

```bash
yarn add @backstage/backend-dynamic-feature-service
```

2. **Add one line to your backend**:

```ts
import { createBackend } from '@backstage/backend-defaults';
import { dynamicPluginsFeatureLoader } from '@backstage/backend-dynamic-feature-service';

const backend = createBackend();
// Add this line to enable dynamic plugin support:
backend.add(dynamicPluginsFeatureLoader);
// ... your other plugins
backend.start();
```

### Configuration

Configure the dynamic plugins directory in your `app-config.yaml`:

```yaml
dynamicPlugins:
  rootDirectory: dynamic-plugins-root
```

## Dynamic Plugin Packaging

### Package Structure Requirements

Before exploring specific packaging approaches, it's important to understand what constitutes a valid dynamic plugin package. Dynamic plugins follow the basic rules of Node.js packages, including:

**Package contents:**

- **package.json**: Package metadata and configuration
- **JavaScript files**: Either loadable by the Node.js CommonJS module loaders for backend plugins, or RSPack/webpack assets for frontend plugins
- **Configuration schema** (optional): JSON schema file (typically `dist/.config-schema.json`) for plugin configuration validation
- **Private dependencies** (optional): Embedded `node_modules` folder containing private dependencies for backend plugins

**Required package.json fields:**

- `name`: Package identifier
- `version`: Package version
- `main`: Entry point to the plugin's JavaScript code
- `backstage.role`: Must be set to `"backend-plugin"` or `"backend-plugin-module"`

### Packaging Approaches

Since this service only handles loading, you would choose a packaging approach based on your plugin's dependencies:

### 1. Simple npm pack

**When to use:** Plugin only uses dependencies that are already provided by the main Backstage application.

**How to use:**

```bash
cd my-backstage-plugin
yarn pack
# Results in: package.tgz

# Extract to dynamic plugins directory
mkdir -p /path/to/dynamic-plugins-root/my-backstage-plugin
tar -xzf package.tgz -C /path/to/dynamic-plugins-root/my-backstage-plugin --strip-components=1
```

**Why this works:** The plugin can resolve all its dependencies from the main application's `node_modules`.

**Reality:** Most plugins have private dependencies not available in the main application, so this approach has limited applicability.

### 2. Manual packaging with dependency installation

**When to use:** Plugin has private dependencies not available in the main Backstage application.

**How to use:**

```bash
# Package the plugin
cd my-backstage-plugin
yarn pack

# Extract and install dependencies
mkdir -p /path/to/dynamic-plugins-root/my-backstage-plugin
tar -xzf package.tgz -C /path/to/dynamic-plugin-root/my-backstage-plugin --strip-components=1
cp yarn.lock /path/to/dynamic-plugin-root/my-backstage-plugin
cd /path/to/dynamic-plugins-root/my-backstage-plugin
yarn install  # Installs all the plugin's dependencies
```

**Why this works:** Each plugin gets its own `node_modules` directory with all its dependencies.

**Example scenario:** Plugin needs `axios@1.4.0` which isn't available in the main application.

### 3. Dedicated bundling CLI command

**When to use:**

- When you want to produce self-contained dynamic plugin packages that can be directly extracted and loaded without any post-action,
- especially when your plugin depends on other packages in the same monorepo.

**How to use:**

The [`backstage-cli package bundle`](../cli/cli-report.md) command automates the required steps. Run it from within a plugin directory:

```bash
cd my-backstage-plugin
yarn backstage-cli package bundle --output-destination /path/to/dynamic-plugins-root
# Creates a self-contained bundle in the /path/to/dynamic-plugins-root/my-backstage-plugin/ sub-folder
```

**Batch bundling:** When bundling many plugins from the same monorepo, use `--pre-packed-dir` to avoid redundant work:

```bash
# First, build a shared dist workspace
backstage-cli build-workspace dist-workspace --alwaysPack ...plugin-packages

# Then bundle each plugin using the pre-packed output
cd plugins/my-backstage-plugin
backstage-cli package bundle --pre-packed-dir ../../dist-workspace
```

See the full list of options in the [CLI reference](../cli/cli-report.md).

**What the command does:**

1. **Builds the plugin** — Produces CJS output for the backend plugin and its transitively-required monorepo packages (`*-node` or `*-common`)
2. **Packs local packages** — Resolves `workspace:^` and `backstage:^` dependencies on both the main plugin package and its transitively-required monorepo packages (`*-node` or `*-common`)
3. **Installs private dependencies** — Seeds a lockfile from the plugin source monorepo and prunes it, then installs a private `node_modules` containing all required dependencies
4. **Collects configuration schemas** — Gathers plugin config schemas and writes them to `dist/.config-schema.json` so they are available for validation at load time

**Benefits:**

- Self-contained packages with all necessary dependencies
- No post-installation steps required (extract and run)
- Consistent dependency structure across all dynamic plugins
- Automatic configuration schema collection
- Production-ready distribution format

---
_Source: https://npm.io/package/@backstage/backend-dynamic-feature-service · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
