DevTools is a Backstage plugin that surfaces key facts about your portal right in the UI. It gives you a clear view of what is running so you can troubleshoot faster and understand your setup with less guesswork. It lives in the Backstage project.
The Info tab shows details about the host and runtime. You can see the operating system, the Node.js version, the Backstage version, and which packages are present. This helps when you need to confirm versions or compare environments during an incident.The Config tab displays the effective configuration your instance is using. It respects plugin config schemas to mask fields marked as secret when that metadata is present. This makes it easier to review config in one place while keeping sensitive values out of view for most users.
You can add optional tabs to suit your needs. A common addition checks external dependencies from the running instance to show basic reachability. There is also an optional view for unprocessed catalog entities that can help with data quality work. Teams can customize the layout to add or remove tabs as their use cases evolve.If you run a self hosted Backstage instance, DevTools gives you a quick way to see what is really deployed and how it is configured. It is a simple way to cut the time you spend chasing environment drift.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install the backend plugin
The backend supports only the new backend system.
- 
Add the package to your backend yarn --cwd packages/backend add @backstage/plugin-devtools-backend
- 
Register the backend plugin // packages/backend/src/index.ts import { createBackend } from '@backstage/backend-defaults'; const backend = createBackend(); // other feature registrations backend.add(import('@backstage/plugin-devtools-backend')); backend.start();
Add the frontend plugin
- 
Add the package to your frontend app yarn --cwd packages/app add @backstage/plugin-devtools
- 
Add the DevTools page route // packages/app/src/App.tsx import React from 'react'; import { Route } from 'react-router'; import { FlatRoutes } from '@backstage/core-app-api'; import { DevToolsPage } from '@backstage/plugin-devtools'; export default function App() { return ( <FlatRoutes> {/* other routes */} <Route path="/devtools" element={<DevToolsPage />} /> </FlatRoutes> ); }
- 
Add a sidebar link // packages/app/src/components/Root/Root.tsx import React from 'react'; import { SidebarPage, Sidebar, SidebarItem, SidebarDivider, SidebarSpace, SidebarSettings } from '@backstage/core-components'; import BuildIcon from '@material-ui/icons/Build'; export const Root = ({ children }: { children?: React.ReactNode }) => ( <SidebarPage> <Sidebar> {/* other sidebar items */} <SidebarSettings /> <SidebarItem icon={BuildIcon} to="devtools" text="DevTools" /> <SidebarDivider /> <SidebarSpace /> </Sidebar> {children} </SidebarPage> );
Optional customize the DevTools tabs
You can choose which tabs to show. You can also add the External Dependencies tab.
- 
Create a custom page component // packages/app/src/components/devtools/CustomDevToolsPage.tsx import { ConfigContent, ExternalDependenciesContent, InfoContent, DevToolsLayout, } from '@backstage/plugin-devtools'; export const DevToolsPage = () => { return ( <DevToolsLayout> <DevToolsLayout.Route path="info" title="Info"> <InfoContent /> </DevToolsLayout.Route> <DevToolsLayout.Route path="config" title="Config"> <ConfigContent /> </DevToolsLayout.Route> <DevToolsLayout.Route path="external-dependencies" title="External Dependencies" > <ExternalDependenciesContent /> </DevToolsLayout.Route> </DevToolsLayout> ); }; export const customDevToolsPage = <DevToolsPage />;
- 
Use the custom page in your route // packages/app/src/App.tsx import React from 'react'; import { Route } from 'react-router'; import { FlatRoutes } from '@backstage/core-app-api'; import { DevToolsPage } from '@backstage/plugin-devtools'; import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; export default function App() { return ( <FlatRoutes> {/* other routes */} <Route path="/devtools" element={<DevToolsPage />}> {customDevToolsPage} </Route> </FlatRoutes> ); }
Optional add the Catalog Unprocessed Entities tab
Follow the setup for that plugin in its docs, then add its content as a tab.
// packages/app/src/components/devtools/CustomDevToolsPage.tsx
import {
  ConfigContent,
  ExternalDependenciesContent,
  InfoContent,
  DevToolsLayout,
} from '@backstage/plugin-devtools';
import { UnprocessedEntitiesContent } from '@backstage/plugin-catalog-unprocessed-entities';
export const DevToolsPage = () => {
  return (
    <DevToolsLayout>
      <DevToolsLayout.Route path="info" title="Info">
        <InfoContent />
      </DevToolsLayout.Route>
      <DevToolsLayout.Route path="config" title="Config">
        <ConfigContent />
      </DevToolsLayout.Route>
      <DevToolsLayout.Route
        path="external-dependencies"
        title="External Dependencies"
      >
        <ExternalDependenciesContent />
      </DevToolsLayout.Route>
      <DevToolsLayout.Route
        path="unprocessed-entities"
        title="Unprocessed Entities"
      >
        <UnprocessedEntitiesContent />
      </DevToolsLayout.Route>
    </DevToolsLayout>
  );
};
export const customDevToolsPage = <DevToolsPage />;Optional permissions on the sidebar item
- 
Add the common package to the frontend yarn --cwd packages/app add @backstage/plugin-devtools-common
- 
Wrap the sidebar item // packages/app/src/components/Root/Root.tsx import React from 'react'; import { SidebarPage, Sidebar, SidebarItem, SidebarDivider, SidebarSpace, SidebarSettings } from '@backstage/core-components'; import BuildIcon from '@material-ui/icons/Build'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { devToolsAdministerPermission } from '@backstage/plugin-devtools-common'; export const Root = ({ children }: { children?: React.ReactNode }) => ( <SidebarPage> <Sidebar> <SidebarSettings /> <RequirePermission permission={devToolsAdministerPermission} errorPage={<></>}> <SidebarItem icon={BuildIcon} to="devtools" text="DevTools" /> </RequirePermission> <SidebarDivider /> <SidebarSpace /> </Sidebar> {children} </SidebarPage> );
Optional permissions on the route
- 
Add the common package to the frontend if you did not already yarn --cwd packages/app add @backstage/plugin-devtools-common
- 
Wrap the route element // packages/app/src/App.tsx import React from 'react'; import { Route } from 'react-router'; import { FlatRoutes } from '@backstage/core-app-api'; import { DevToolsPage } from '@backstage/plugin-devtools'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { devToolsAdministerPermission } from '@backstage/plugin-devtools-common'; export default function App() { return ( <FlatRoutes> {/* other routes */} <Route path="/devtools" element={ <RequirePermission permission={devToolsAdministerPermission}> <DevToolsPage /> </RequirePermission> } /> </FlatRoutes> ); }
- 
If you use a custom page include the children as well // packages/app/src/App.tsx import React from 'react'; import { Route } from 'react-router'; import { FlatRoutes } from '@backstage/core-app-api'; import { DevToolsPage } from '@backstage/plugin-devtools'; import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { devToolsAdministerPermission } from '@backstage/plugin-devtools-common'; export default function App() { return ( <FlatRoutes> {/* other routes */} <Route path="/devtools" element={ <RequirePermission permission={devToolsAdministerPermission}> <DevToolsPage /> </RequirePermission> } > {customDevToolsPage} </Route> </FlatRoutes> ); }
Optional permission policy on the backend
- 
Add the common package to the backend yarn --cwd packages/backend add @backstage/plugin-devtools-common
- 
Use the permissions in your backend policy // packages/backend/src/plugins/permission.ts import { devToolsAdministerPermission, devToolsConfigReadPermission, devToolsExternalDependenciesReadPermission, devToolsInfoReadPermission, } from '@backstage/plugin-devtools-common'; import { AuthorizeResult, isPermission, PermissionPolicy, PolicyDecision, PolicyQuery, } from '@backstage/plugin-permission-node'; class TestPermissionPolicy implements PermissionPolicy { async handle(request: PolicyQuery): Promise<PolicyDecision> { const user = request.identity; if (isPermission(request.permission, devToolsAdministerPermission)) { if (user?.identity.ownershipEntityRefs.includes('group:default/backstage-admins')) { return { result: AuthorizeResult.ALLOW }; } return { result: AuthorizeResult.DENY }; } if (isPermission(request.permission, devToolsInfoReadPermission)) { if (user?.identity.ownershipEntityRefs.includes('group:default/backstage-admins')) { return { result: AuthorizeResult.ALLOW }; } return { result: AuthorizeResult.DENY }; } if (isPermission(request.permission, devToolsConfigReadPermission)) { if (user?.identity.ownershipEntityRefs.includes('group:default/backstage-admins')) { return { result: AuthorizeResult.ALLOW }; } return { result: AuthorizeResult.DENY }; } if (isPermission(request.permission, devToolsExternalDependenciesReadPermission)) { if (user?.identity.ownershipEntityRefs.includes('group:default/backstage-admins')) { return { result: AuthorizeResult.ALLOW }; } return { result: AuthorizeResult.DENY }; } return { result: AuthorizeResult.ALLOW }; } }
Optional config for the Info tab package list
# app-config.yaml
devTools:
  info:
    packagePrefixes:
      - '@roadiehq/backstage-'
      - '@spotify/backstage-'Optional config for the External Dependencies tab
# app-config.yaml
devTools:
  externalDependencies:
    endpoints:
      - name: 'Google'
        type: 'fetch'
        target: 'https://google.ca'
      - name: 'Google Public DNS'
        type: 'ping'
        target: '8.8.8.8'If you use the ping type you must ensure the host OS has a ping binary. For example in a Debian based image
RUN  \
    apt-get update && \
    apt-get install -y iputils-pingDocker tip for Backstage version reporting
Copy the backstage.json file into the image so the Info tab can show the Backstage version.
WORKDIR /app
ENV NODE_ENV=production
COPY  . .
# ensure backstage.json ends up in the workdir at runtime
# if you build with a separate step copy it explicitly
# COPY --chown=node:node path/to/backstage.json ./Changelog
This changelog is produced from commits made to the DevTools plugin since a year ago. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.
Breaking changes
- Simplify FrontendPlugin type. Override methods move to the new OverridableFrontendPlugin type. Update any code that uses AppNode overrides to use the new type. #30904 2 months ago
- ApiBlueprint now uses callback param syntax with defineParams. Update blueprint usage to the new pattern. #30673 2 months ago
- Remove the default prefix from blueprint params. Update param names in any custom blueprints. #30721 2 months ago
- Rename the plugin ID option to pluginId in createFrontendPlugin. Update any calls in this repo. #29749 5 months ago
Features
- Add plugin info support. Plugins can expose metadata from package JSON and an optional manifest. Apps can customize how this is read. #29953 4 months ago
- Add a new repo start command for local workspaces. It can start the app and backend together or a single target. This helps when running a community plugin in isolation. #29489 5 months ago
Documentation
- Recommend the name defineParams for the blueprint param helper. This lines up naming across definition and use. #30714 2 months ago
Maintenance
- Avoid forwarding ConfigurableExtensionDataRef in types. Cleans up API reports. #30765 2 months ago
- Update React imports to the modern JSX transform. Prepares for React nineteen. #29499 5 months ago
- Bump TypeScript to five point six. #29090 7 months ago
- Sort extension definition in plugin types for more stable API reports. #29061 7 months ago
- Fix API report warning validation. Reports now warn as expected. #27116 11 months ago
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.