Backstage TechDocs is the documentation system built into Backstage. You write docs in Markdown next to your code. A build step turns that content into static pages with MkDocs. Backstage renders those pages inside your portal so docs live with your services and APIs. Search and ownership metadata come through the Catalog. You get one place to read, update, and discover technical docs.
The plugin works in two parts. A reader in the app displays prebuilt pages. A service fetches the generated files from storage. You can generate in CI or on a central worker. The Addon Framework lets you add small features at read time such as report an issue or custom navigation. Common uses include service guides, runbooks, ADRs, onboarding notes, and internal standards.
ELCA writes that “through the TechDocs plugin, we brought a consistent and role oriented documentation.” Their story is here Platform engineering with Backstage at ELCA.
If you run a self hosted Backstage, TechDocs gives you a simple path to publish docs-like-code and keep them close to everything your engineers ship.
Installation Instructions
These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.
Install the frontend package
yarn workspace app add @backstage/plugin-techdocs
Add TechDocs routes so users can browse docs.
Edit packages/app/src/App.tsx
// packages/app/src/App.tsx
import React from 'react';
import { FlatRoutes } from '@backstage/core-app-api';
import { Route } from 'react-router';
import { TechDocsIndexPage, TechDocsReaderPage } from '@backstage/plugin-techdocs';
// keep your other imports
export default function App() {
return (
<FlatRoutes>
{/* your other routes */}
<Route path="/docs" element={<TechDocsIndexPage />} />
<Route path="/docs/:namespace/:kind/:name/*" element={<TechDocsReaderPage />} />
</FlatRoutes>
);
}
Show TechDocs on each entity page.
Edit packages/app/src/components/catalog/EntityPage.tsx
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import { EntityLayout } from '@backstage/plugin-catalog';
import { EntityTechdocsContent } from '@backstage/plugin-techdocs';
// keep your other imports
export const serviceEntityPage = (
<EntityLayout>
{/* your other tabs */}
<EntityLayout.Route path="/docs" title="Docs">
<EntityTechdocsContent />
</EntityLayout.Route>
</EntityLayout>
);
Mark an entity with TechDocs
Add the annotation to an entity in your catalog.
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: example
annotations:
backstage.io/techdocs-ref: dir:.
spec:
type: service
owner: guests
lifecycle: production
Add the backend plugin with the new backend system
Install the backend package.
yarn workspace backend add @backstage/plugin-techdocs-backend
Register the plugin in the backend.
Edit packages/backend/src/index.ts
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// other backend features
backend.add(import('@backstage/plugin-techdocs-backend'));
backend.start();
Add minimal config. Local builder and local publisher are the simplest path.
Edit app-config.yaml
techdocs:
builder: local
publisher:
type: local
backend:
baseUrl: http://localhost:7007
listen:
host: 0.0.0.0
port: 7007
You can switch publisher type to a cloud storage later. The plugin supports googleGcs awsS3 azureBlobStorage and openStackSwift.
Add the backend plugin with the old backend system
This backend version needs a plugin release that still exports a router. Use a version before 2.0.0.
Install a compatible version.
yarn workspace backend add @backstage/plugin-techdocs-backend@^1.11.6
Create the plugin file.
Create packages/backend/src/plugins/techdocs.ts
// packages/backend/src/plugins/techdocs.ts
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import {
createRouter,
Generators,
Preparers,
Publisher,
} from '@backstage/plugin-techdocs-backend';
import { DockerContainerRunner } from '@backstage/backend-common';
import Docker from 'dockerode';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const preparers = await Preparers.fromConfig(env.config, {
logger: env.logger,
reader: env.reader,
});
const dockerClient = new Docker();
const containerRunner = new DockerContainerRunner({ dockerClient });
const generators = await Generators.fromConfig(env.config, {
logger: env.logger,
containerRunner,
});
const publisher = await Publisher.fromConfig(env.config, {
logger: env.logger,
discovery: env.discovery,
});
await publisher.getReadiness();
return await createRouter({
preparers,
generators,
publisher,
logger: env.logger,
config: env.config,
discovery: env.discovery,
cache: env.cache,
});
}
Mount the router.
Edit packages/backend/src/index.ts
// packages/backend/src/index.ts
import techdocs from './plugins/techdocs';
// keep your existing imports and setup
async function main() {
// create env like in your app
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
const apiRouter = Router();
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
// mount apiRouter on your service
}
Add minimal config.
Edit app-config.yaml
techdocs:
builder: local
publisher:
type: local
Where users will see TechDocs
- The Docs tab on each entity page shows docs for that entity
- The Docs root path shows the index page at the path
/docs
in the frontend app
Notes on storage and build setup
- Local publisher serves files from the backend service
- Cloud storage publishers are supported when you are ready to externalize docs
- The builder defaults to local which triggers builds on demand in the backend process
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.