PagerDuty logo

Backstage PagerDuty Plugin

Created by @samiramkr

PagerDuty is an incident management platform that alerts the right people, coordinates response, and helps teams resolve issues faster. Many engineering teams use it to manage on call rotations and escalations every day.

The PagerDuty plugin brings that context into Backstage. On a service page you can see current incidents, start a new incident, review recent change events, and check who is on call without switching tools. A backend module centralizes calls to the PagerDuty APIs to improve security and performance. The plugin can also sync service dependencies between your Backstage catalog and PagerDuty so relationships stay accurate on both sides. It supports mapping existing PagerDuty services to Backstage entities and creating new services from your software templates.

This helps during an outage because you can confirm impact, page the right responder, and keep work visible in one place. It also helps during onboarding by making PagerDuty setup part of your templates so new services follow the same standards from day one.

PagerDuty incidents for sample-service-1 rendered in Backstage.

Installation Instructions

These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.

Install the packages

  1. Add the frontend package to your app

    Copy
    yarn add --cwd packages/app @backstage/plugin-pagerduty
  2. Add the backend package to your backend

    Copy
    yarn add --cwd packages/backend @pagerduty/backstage-plugin-backend

Register the frontend API client

  1. Open packages/app/src/apis.ts

  2. Register the PagerDuty API client

    Copy
    // packages/app/src/apis.ts
    import {
      configApiRef,
      createApiFactory,
      discoveryApiRef,
      fetchApiRef,
    } from '@backstage/core-plugin-api';
    import {
      pagerDutyApiRef,
      PagerDutyClient,
    } from '@backstage/plugin-pagerduty';
    
    export const apis = [
      // keep existing factories
      createApiFactory({
        api: pagerDutyApiRef,
        deps: {
          discoveryApi: discoveryApiRef,
          fetchApi: fetchApiRef,
          configApi: configApiRef,
        },
        factory: ({ discoveryApi, fetchApi, configApi }) =>
          PagerDutyClient.fromConfig(configApi, { discoveryApi, fetchApi }),
      }),
    ];

Add the PagerDuty card to the Service Entity page

  1. Open packages/app/src/components/catalog/EntityPage.tsx

  2. Import the card and the entity guard

    Copy
    import {
      EntityPagerDutyCard,
      isPagerDutyAvailable,
    } from '@backstage/plugin-pagerduty';
    import {
      EntitySwitch,
    } from '@backstage/plugin-catalog-react';
  3. Render the card on the Service overview

    Copy
    // inside the Service Entity page route
    // example layout
    <EntityLayout>
      <EntityLayout.Route path="/" title="Overview">
        <Grid container spacing={3} alignItems="stretch">
          <Grid item xs={12} md={6}>
            <EntitySwitch>
              <EntitySwitch.Case if={isPagerDutyAvailable}>
                <EntityPagerDutyCard />
                {/*
                  Optional props
                  <EntityPagerDutyCard readOnly />
                  <EntityPagerDutyCard disableChangeEvents />
                */}
              </EntitySwitch.Case>
            </EntitySwitch>
          </Grid>
        </Grid>
      </EntityLayout.Route>
    </EntityLayout>

Add the PagerDuty card to the Home page optional

  1. Open packages/app/src/components/home/HomePage.tsx

  2. Import the card

    Copy
    import { HomePagePagerDutyCard } from '@backstage/plugin-pagerduty';
  3. Render it on the page

    Copy
    <Grid container spacing={3}>
      <Grid item xs={12} md={6}>
        <HomePagePagerDutyCard
          // Provide either integrationKey or serviceId plus a name
          // Example using serviceId
          name="Payments Service"
          serviceId="P123456"
          // Optional readOnly
          // readOnly
        />
      </Grid>
    </Grid>

Add a Trigger Incident button optional

  1. Import the button

    Copy
    import { TriggerButton } from '@backstage/plugin-pagerduty';
  2. Place it where it fits your UI

    Copy
    <TriggerButton>
      Create PagerDuty incident
    </TriggerButton>

Configure entity annotations

  1. Add one of these annotations to each Service entity yaml

    Using service id

    Copy
    metadata:
      annotations:
        pagerduty.com/service-id: P123456

    Using integration key

    Copy
    metadata:
      annotations:
        pagerduty.com/integration-key: abcdef0123456789

    If both are present the integration key takes precedence

Set secrets for the backend

  1. Set these environment variables in the backend process
    Copy
    export PAGERDUTY_CLIENT_ID=your_oauth_client_id
    export PAGERDUTY_CLIENT_SECRET=your_oauth_client_secret
    export PAGERDUTY_SUBDOMAIN=your_pd_subdomain

Wire the backend on the classic backend system

  1. Create a router factory

    Copy
    // packages/backend/src/plugins/pagerduty.ts
    import { Logger } from 'winston';
    import { PluginEnvironment } from '../types';
    import { createRouter } from '@pagerduty/backstage-plugin-backend';
    
    export default async function createPlugin(
      env: PluginEnvironment,
    ) {
      return await createRouter({
        logger: env.logger as Logger,
        config: env.config,
        discovery: env.discovery,
        tokenManager: env.tokenManager,
      });
    }
  2. Mount the router in the backend index

    Copy
    // packages/backend/src/index.ts
    import { createServiceBuilder } from '@backstage/backend-common';
    import { useHotMemoize } from '@backstage/backend-common';
    import { createEnv } from './lib/createEnv';
    
    async function main() {
      const env = await createEnv('backend');
    
      const apiRouter = Router();
    
      const pagerdutyEnv = useHotMemoize(module, () => createEnv('pagerduty'));
      const pagerdutyRouter = await require('./plugins/pagerduty').default(pagerdutyEnv);
      apiRouter.use('/pagerduty', await pagerdutyRouter);
    
      const service = createServiceBuilder(module)
        .setPort(env.backend.port)
        .addRouter('/api', apiRouter);
    
      await service.start();
    }
    
    main().catch(err => {
      process.exit(1);
    });

    The router mounts at path api/pagerduty

Wire the backend on the new backend system

  1. Mount the same router using the new backend APIs

    Copy
    // packages/backend/src/index.ts
    import { createBackend } from '@backstage/backend-defaults';
    import {
      coreServices,
      createBackendPlugin,
    } from '@backstage/backend-plugin-api';
    import { createRouter as createPagerDutyRouter } from '@pagerduty/backstage-plugin-backend';
    
    const backend = createBackend();
    
    backend.add(
      createBackendPlugin({
        pluginId: 'pagerduty',
        register(env) {
          env.registerInit({
            deps: {
              httpRouter: coreServices.httpRouter,
              logger: coreServices.logger,
              config: coreServices.rootConfig,
              discovery: coreServices.discovery,
              tokenManager: coreServices.tokenManager,
            },
            async init({
              httpRouter,
              logger,
              config,
              discovery,
              tokenManager,
            }) {
              const router = await createPagerDutyRouter({
                logger,
                config,
                discovery,
                tokenManager,
              });
              httpRouter.use('/pagerduty', router);
            },
          });
        },
      }),
    );
    
    backend.start();

    The router mounts at path api/pagerduty

Run the app

  1. Start the backend in one terminal

    Copy
    yarn start-backend
  2. Start the frontend in another terminal

    Copy
    yarn start

Things to Know

The PagerDuty plugin is a frontend plugin that provides convenient access to frequently used PagerDuty capabilities. Developers see pertinent information and actions for every entity that is connected to a PagerDuty service including:

  • who is currently on call for the entity
  • whether there are any active incidents for the entity
  • raise a new incident for the entity

Connecting an entity to a PagerDuty service

An entity is connected to a PagerDuty service by adding a pagerduty.com/integration-key annotation to the entity’s catalog-info.yaml file. For example:

Copy
annotations:
  pagerduty.com/integration-key: a8642af33a984c07d01144f420df074e

The integration key can be retrieved from the Integrations tab of the service in the PagerDuty service directory.

PagerDuty service integrations tab

PagerDuty UI placement and rendering

In the example above, the PagerDuty UI was added to the Overview tab for all entities. The PagerDuty panel was conditionally rendered only for entities that are actual connected to a PagerDuty service using the isPagerDutyAvailable function.

You are not constrained to displaying the PagerDuty panel on the Overview tab. Where and how you display the PagerDuty panel is completely within your control. For example, what if your organization has a policy that requires all APIs to have on call support? Rather than hiding the PagerDuty panel when an API entity is not connected to a PagerDuty service, you could display a policy reminder. Not only can Backstage provide access to frequently used PagerDuty capabilities but it can also help bring visibility to policy gaps.

Changelog

This changelog is produced from commits made to the PagerDuty plugin since 3 months ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.

Maintenance

  • Change module main entry #58 merged 2 months ago
  • Add eslint config and format the repo #53 merged 2 months ago
  • Add CODEOWNERS file for repository ownership #73 merged 1 month ago

Documentation

  • Update documentation #50 merged 2 months ago
  • Merge Next update documentation #15 merged 2 months ago
  • Update documentation #14 merged 2 months ago

Set up Backstage in minutes with Roadie