Custom Dynamic Plugins
Custom Dynamic Plugins
DevPortal supports dynamic plugins that can be enabled, disabled, and configured at runtime without rebuilding the container image. This guide shows you how to enable and customize plugin behavior using a custom dynamic-plugins.yaml file mount by docker compose.
Dynamic plugins are a deep subject on their own. Please refer to the Plugins section for more information.
Understanding Dynamic Plugins
The image includes a catalog of optional plugins, all disabled by default. The core chrome plugins (global header, homepage, About) are pre-installed and enabled by default — they are listed in the shipped dynamic-plugins.yaml. You activate plugins via presets or a mounted dynamic-plugins.yaml. You add operator-level overrides by mounting a single file:
dynamic-plugins.yaml(mounted at runtime): a top-levelplugins:list. Mounting this file replaces the image's/app/dynamic-plugins.yaml— it does not merge with it. The entrypoint keeps yourplugins:entries and assembles the internalincludes:chain (marketplace state + preset fragments) itself, so you never reference the image's default file. Because it replaces: the shipped file already lists the core chrome plugins (global header, homepage, About) — keep those entries when you add your own, or those frontend plugins stop surfacing.
Creating a Custom Plugins File
Create a dynamic-plugins.yaml file in your project directory. In this file you can simply enable pre-installed plugins (and use them with their default settings), disable them, or download and configure plugins from NPM or OCI registries.
All pre-installed plugins are available in the container image at /app/dynamic-plugins-root/ - you can use relative paths to them. Downloaded plugins will be referenced by their NPM or OCI registry URL and fetched at runtime.
plugins:
# Enable a pre-installed plugin with its default settings
- package: './dynamic-plugins/dist/some-plugin-dynamic'
disabled: false
# Disable a plugin
- package: './dynamic-plugins/dist/another-plugin-dynamic'
disabled: true
# Download and configure plugin with custom settings
- package: '@someorg/custom-plugin-dynamic'
disabled: false
pluginConfig:
here:
goes: xxx
some: yyy
config: yyy
Mounting with Docker Run
docker run --rm --name devportal -d \
-p 7007:7007 \
-v $(pwd)/dynamic-plugins.yaml:/app/dynamic-plugins.yaml:ro \
veecode/devportal:2.1.3
Mounting with Docker Compose
Update your docker-compose.yml:
services:
devportal:
image: veecode/devportal:2.1.3
ports:
- "7007:7007"
volumes:
- ./dynamic-plugins.yaml:/app/dynamic-plugins.yaml:ro
Loading External Plugins
You can also load plugins from external registries (NPM or OCI):
plugins:
# Load from NPM registry
- package: '@someorg/my-custom-plugin@1.0.0'
disabled: false
# Load from OCI registry
- package: 'oci://ghcr.io/my-org/my-plugin:latest'
disabled: false
How dynamic-plugins.yaml is assembled
You provide only a top-level plugins: list. You do not write an includes: key — the entrypoint owns it. On every boot it copies your dynamic-plugins.yaml to a writable shadow and rebuilds the includes: chain itself (marketplace state + any preset fragments); any includes: you add is replaced. dynamic-plugins.default.yaml is documentation only (a vitrine) and is not part of the runtime chain.
What it does not do is merge your plugins: list with the image's. A mounted file replaces /app/dynamic-plugins.yaml wholesale, so the core chrome plugins (global header, homepage, About) survive only because the shipped file lists them. Keep those entries in your file, then add your own:
plugins:
# keep the shipped core-chrome entries, then add your overrides below
- package: './dynamic-plugins/dist/some-plugin-dynamic'
disabled: false
After the plugin install script runs, it generates dynamic-plugins-root/app-config.dynamic-plugins.yaml from the pluginConfig blocks of all enabled plugins. This generated file loads after your app-config.local.yaml (and before app-config.saas.yaml in SaaS deployments).
extensions-install.yaml
You may notice an extensions-install.yaml file in the working directory. This is a write-through cache for marketplace plugin installation state — the database is the source of truth and the Node app regenerates this file on every change. The Python install script reads it at startup. Do not delete it; if it is missing, the entrypoint creates an empty one automatically.
Important Notes
Dynamic plugins are a deep subject on their own. Please refer to the Plugins section for more information. The dynamic plugins feature is based on the same plugin system used by Red Hat Developer Hub, so Red Hat documentation is also a good resource on this topic.
Dynamic plugins require a special kind of packaging. All DevPortal pre-installed dynamic plugins are published on the public NPM registry and pulled at build time into the veecode/devportal distro image. Not all plugins are available as dynamic plugins, so please check each plugin's documentation to see if it is available as such. There is usually a -dynamic suffix in a dynamic plugin package name, and they tend to exist in both forms in the NPM registry.
Examples
OCI plugins are fetched via skopeo at boot. See the Dynamic Plugins section for OCI registry usage examples.
Viewing Available Plugins
To see which plugins are pre-installed in your image, check the logs when the container starts:
docker logs devportal | grep "dynamic-plugins"