Skip to main content

Migrating your Backend to the New Backend System

Overview

This section describes how to migrate an existing Backstage backend service package (typically in packages/backend) to use the new backend system.

One of the main benefits of the new backend system is that it abstracts away the way that plugins and their dependencies are wired up, leading to a significantly simplified backend package that rarely if ever needs to change when plugins or their dependencies evolve. You generally don't have to convert all of your internal plugins and support classes themselves to the backend system first - the migration here will mostly deal with wiring and using compatibility wrappers where possible in the backend package itself. We hope that you will find that you end up with a much smaller, easier to understand, and easier to maintain package as a result of these steps, and then being able to migrate plugins as a separate endeavour later.

Overall Structure

Your typical backend package has a few overall component parts:

  • An index.ts file that houses all of the creation and wiring together of all of the plugins and their dependencies
  • A types.ts file that defines the "environment", i.e. the various dependencies that get created by the backend and passed down into each plugin
  • A plugins folder which has one file for each plugin, e.g. plugins/catalog.ts

The index file has this overall shape:

import todo from './plugins/todo'; // repeated for N plugins

function makeCreateEnv(config: Config) {
return (plugin: string): PluginEnvironment => {
// ... build per-plugin environment
};
}

async function main() {
// ... early init
const createEnv = makeCreateEnv(config);
const todoEnv = useHotMemoize(module, () => createEnv('todo')); // repeated for N plugins
const apiRouter = Router();
apiRouter.use('/todo', await todo(todoEnv)); // repeated for N plugins
// ... wire up and start http server
}

module.hot?.accept();
main().catch(...);

Migrating the Index File

This migration will try to leave the plugins folder unchanged initially, first focusing on removing the environment type and reducing the index file to its bare minimum. Then as a later step, we can reduce the plugins folder bit by bit, replacing those files generally with one-liners in the index file instead.

Let's start by establishing the basis of your new index file. You may want to comment out its old contents, or renaming the old file to index.backup.ts for reference and making a new blank one to work on - whichever works best for you. These are our new blank contents in the index file:

packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();
backend.start();

Note that the environment builder and the main dance are entirely gone.

We'll also want to add some backend system packages as dependencies. Run the following command:

# from the repository root
yarn --cwd packages/backend add @backstage/backend-defaults @backstage/backend-plugin-api

You should now be able to start this up with the familiar yarn workspace backend start command locally and seeing some logs scroll by. But it'll just be a blank service with no real features added. So let's stop it with Ctrl+C and reintroduce some plugins into the mix.

packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import { legacyPlugin } from '@backstage/backend-common';

const backend = createBackend();
backend.add(legacyPlugin('todo', import('./plugins/todo')));
backend.start();

The todo plugin used above is just an example and you may not have it enabled in your own backend. Feel free to change it to some other plugin that you actually have in your plugins folder, for example backend.add(legacyPlugin('catalog', import('./plugins/catalog'))).

The legacyPlugin helper makes it easy to bridge the gap between the old-style plugin files and the new backend system. It ensures that the dependencies that you used to have to declare by hand in your env are gathered behind the scenes, then passes them into the relevant createPlugin export function, and makes sure that the route handler it returns is passed into the HTTP router with the given prefix.

Handling Custom Environments

In the simple case, what we did above is sufficient, TypeScript is happy, and the backend runs with the new feature. If they do, feel free to skip this entire section, and delete types.ts.

Sometimes though, type errors can be reported on the newly added line, saying that parts of the PluginEnvironment type do not match. This happens when the environment was changed from the defaults, perhaps with your own custom additions. If this is the case in your installation, you still aren't out of luck - you can build a customized legacyPlugin function.

packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import { legacyPlugin } from '@backstage/backend-common';
import {
makeLegacyPlugin,
loggerToWinstonLogger,
} from '@backstage/backend-common';
import { coreServices } from '@backstage/backend-plugin-api';

const legacyPlugin = makeLegacyPlugin(
{
cache: coreServices.cache,
config: coreServices.rootConfig,
database: coreServices.database,
discovery: coreServices.discovery,
logger: coreServices.logger,
permissions: coreServices.permissions,
scheduler: coreServices.scheduler,
tokenManager: coreServices.tokenManager,
reader: coreServices.urlReader,
identity: coreServices.identity,
// ... and your own additions
},
{
logger: log => loggerToWinstonLogger(log),
},
);

const backend = createBackend();
backend.add(legacyPlugin('todo', import('./plugins/todo')));
backend.start();

The first argument to makeLegacyPlugin is the mapping from environment keys to references to actual backend system services. The second argument allows you to "tweak" the types of those services to something more fitting to your env. For example, you'll see that the logger service API type was changed from the raw Winston logger of old, to a different, custom API, so we use a helper function to transform that particular one.

To make additions as mentioned above to the environment, you will start to get into the weeds of how the backend system wiring works. You'll need to have a service reference and a service factory that performs the actual creation of your service. Please see the services article to learn how to create a service ref and its default factory. You can place that code directly in the index file for now if you want, or near the actual implementation class in question.

In this example, we'll assume that your added environment field is named example, and the created ref is named exampleServiceRef.

packages/backend/src/index.ts
import { exampleServiceRef } from '<somewhere>'; // if the definition is elsewhere

const legacyPlugin = makeLegacyPlugin(
{
// ... the above core services still go here
example: exampleServiceRef,
},
{
logger: log => loggerToWinstonLogger(log),
},
);

After this, your backend will know how to instantiate your thing on demand and place it in the legacy plugin environment.

Note

If you happen to be dealing with a service ref that does NOT have a default implementation, but rather has a separate service factory, then you will also need to import that factory and pass it to the services array argument of createBackend.

Cleaning Up the Plugins Folder

For plugins that are private and your own, you can follow a dedicated migration guide as you see fit, at a later time.

For third party backend plugins, in particular the larger core plugins that are maintained by the Backstage maintainers, you may find that they have already been migrated to the new backend system. This section describes some specific such migrations you can make.

Note

For each of these, note that your backend still needs to have a dependency (e.g. in packages/backend/package.json) to those plugin packages, and they still need to be configured properly in your app-config. Those mechanisms still work just the same as they used to in the old backend system.

The App Plugin

The app backend plugin that serves the frontend from the backend can trivially be used in its new form.

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-app-backend'));

If you need to override the app package name, which otherwise defaults to "app", you can do so via the app.packageName configuration key.

You should be able to delete the plugins/app.ts file at this point.

The Catalog Plugin

A basic installation of the catalog plugin looks as follows.

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);

Note that this also installs the scaffolder module for the catalog, which enables the use of the Template kind. In the event that you do not use templates at all, you can remove that line.

If you have other customizations made to plugins/catalog.ts, such as adding custom processors or entity providers, read on. Otherwise, you should be able to just delete that file at this point.

Amazon Web Services

AwsEksClusterProcessor and AwsOrganizationCloudAccountProcessor have not yet been migrated to the new backend system. See Other Catalog Extensions for how to use these in the new backend system.

For AwsS3DiscoveryProcessor, first migrate to AwsS3EntityProvider.

To migrate AwsS3EntityProvider to the new backend system, add a reference to the @backstage/plugin-catalog-backend-module-aws module.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-aws'));

If you were providing a schedule in code, this now needs to be set via configuration. All other AWS configuration in app-config.yaml remains the same.

app-config.yaml
catalog:
providers:
awsS3:
yourProviderId:
# ...
schedule:
frequency: PT1H
timeout: PT50M

Azure DevOps

For AzureDevOpsDiscoveryProcessor, first migrate to AzureDevOpsEntityProvider.

To migrate AzureDevOpsEntityProvider to the new backend system, add a reference to the @backstage/plugin-catalog-backend-module-azure module.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-azure'));

If you were providing a schedule in code, this now needs to be set via configuration. All other Azure DevOps configuration in app-config.yaml remains the same.

app-config.yaml
catalog:
providers:
azureDevOps:
yourProviderId:
# ...
schedule:
frequency: PT1H
timeout: PT50M

Open API

InternalOpenApiDocumentationProvider has not yet been migrated to the new backend system. See Other Catalog Extensions for how to use this in the new backend system.

Bitbucket

For BitbucketDiscoveryProcessor, migrate to BitbucketCloudEntityProvider or BitbucketServerEntityProvider

To migrate BitbucketCloudEntityProvider to the new backend system, add a reference to the @backstage/plugin-catalog-backend-module-bitbucket-cloud module.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-bitbucket-cloud'));

If you were providing a schedule in code, this now needs to be set via configuration. All other Bitbucket Cloud configuration in app-config.yaml remains the same.

app-config.yaml
catalog:
providers:
bitbucketCloud:
yourProviderId:
# ...
schedule:
frequency: PT30M
timeout: PT3M

To migrate BitbucketServerEntityProvider to the new backend system, add a reference to @backstage/plugin-catalog-backend-module-bitbucket-server.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-bitbucket-server'),
);

If you were providing a schedule in code, this now needs to be set via configuration. All other Bitbucket Server configuration in app-config.yaml remains the same.

app-config.yaml
catalog:
providers:
bitbucketServer:
yourProviderId:
# ...
schedule:
frequency: PT30M
timeout: PT3M

Google Cloud Platform

To migrate GkeEntityProvider to the new backend system, add a reference to @backstage/plugin-catalog-backend-module-gcp.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-gcp'));

Configuration in app-config.yaml remains the same.

Gerrit

To migrate GerritEntityProvider to the new backend system, add a reference to @backstage/plugin-catalog-backend-module-gerrit.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-gerrit'));

If you were providing a schedule in code, this now needs to be set via configuration. All other Gerrit configuration in app-config.yaml remains the same.

app-config.yaml
catalog:
providers:
gerrit:
yourProviderId:
# ...
schedule:
frequency: PT30M
timeout: PT3M

GitHub

For GithubDiscoveryProcessor, GithubMultiOrgReaderProcessor and GithubOrgReaderProcessor, first migrate to the equivalent Entity Provider.

To migrate GithubEntityProvider to the new backend system, add a reference to @backstage/plugin-catalog-backend-module-github.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-github'));

If you were providing a schedule in code, this now needs to be set via configuration. All other GitHub configuration in app-config.yaml remains the same.

app-config.yaml
catalog:
providers:
github:
yourProviderId:
# ...
schedule:
frequency: PT30M
timeout: PT3M

To migrate GithubMultiOrgEntityProvider or GithubOrgEntityProvider to the new backend system, add a reference to @backstage/plugin-catalog-backend-module-github-org.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-github-org'));
GithubOrgEntityProvider

If you were using GithubOrgEntityProvider you might have been configured in code like this:

packages/backend/src/plugins/catalog.ts
// The org URL below needs to match a configured integrations.github entry
// specified in your app-config.
builder.addEntityProvider(
GithubOrgEntityProvider.fromConfig(env.config, {
id: 'production',
orgUrl: 'https://github.com/backstage',
logger: env.logger,
schedule: env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 60 },
timeout: { minutes: 15 },
}),
}),
);

This now needs to be set via configuration. The options defined above are now set in app-config.yaml instead as shown below:

app-config.yaml
catalog:
providers:
githubOrg:
- id: production
githubUrl: 'https://github.com'
orgs: ['backstage']
schedule:
frequency: PT30M
timeout: PT15M
GithubMultiOrgEntityProvider

If you were using GithubMultiOrgEntityProvider you might have been configured in code like this:

packages/backend/src/plugins/catalog.ts
// The GitHub URL below needs to match a configured integrations.github entry
// specified in your app-config.
builder.addEntityProvider(
GithubMultiOrgEntityProvider.fromConfig(env.config, {
id: 'production',
githubUrl: 'https://github.com',
// Set the following to list the GitHub orgs you wish to ingest from. You can
// also omit this option to ingest all orgs accessible by your GitHub integration
orgs: ['org-a', 'org-b'],
logger: env.logger,
schedule: env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 60 },
timeout: { minutes: 15 },
}),
}),
);

This now needs to be set via configuration. The options defined above are now set in app-config.yaml instead as shown below:

app-config.yaml
catalog:
providers:
githubOrg:
- id: production
githubUrl: 'https://github.com'
orgs: ['org-a', 'org-b']
schedule:
frequency: PT30M
timeout: PT15M

If you were providing transformers, these can be configured by extending githubOrgEntityProviderTransformsExtensionPoint

packages/backend/src/index.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { githubOrgEntityProviderTransformsExtensionPoint } from '@backstage/plugin-catalog-backend-module-github-org';

backend.add(
createBackendModule({
pluginId: 'catalog',
moduleId: 'githubOrgTransformers',
register(env) {
env.registerInit({
deps: {
githubOrgTransformers:
githubOrgEntityProviderTransformsExtensionPoint,
},
async init({ githubOrgTransformers }) {
githubOrgTransformers.setUserTransformer(myUserTransformer);
githubOrgTransformers.setTeamTransformer(myTeamTransformer);
},
});
},
}),
);

Microsoft Graph

For MicrosoftGraphOrgReaderProcessor, first migrate to MicrosoftGraphOrgEntityProvider

To migrate MicrosoftGraphOrgEntityProvider to the new backend system, add a reference to @backstage/plugin-catalog-backend-module-msgraph.

packages/backend/src/index.ts
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-msgraph'));

If you were providing a schedule in code, this now needs to be set via configuration. All other Microsoft Graph configuration in app-config.yaml remains the same.

app-config.yaml
catalog:
providers:
microsoftGraphOrg:
provider:
schedule:
frequency: PT4H
timeout: PT30M

If you were providing transformers, these can be configured by extending microsoftGraphOrgEntityProviderTransformExtensionPoint

packages/backend/src/index.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { microsoftGraphOrgEntityProviderTransformExtensionPoint } from '@backstage/plugin-catalog-backend-module-msgraph/alpha';

backend.add(
createBackendModule({
pluginId: 'catalog',
moduleId: 'microsoft-graph-extensions',
register(env) {
env.registerInit({
deps: {
microsoftGraphTransformers:
microsoftGraphOrgEntityProviderTransformExtensionPoint,
},
async init({ microsoftGraphTransformers }) {
microsoftGraphTransformers.setUserTransformer(myUserTransformer);
microsoftGraphTransformers.setGroupTransformer(myGroupTransformer);
microsoftGraphTransformers.setOrganizationTransformer(
myOrganizationTransformer,
);
},
});
},
}),
);

Other Catalog Extensions

You will use the extension points mechanism to extend or tweak the functionality of the plugin. To do that, you'll make your own bespoke module which depends on the appropriate extension point and interacts with it.

packages/backend/src/index.ts
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import { createBackendModule } from '@backstage/backend-plugin-api';

const catalogModuleCustomExtensions = createBackendModule({
pluginId: 'catalog', // name of the plugin that the module is targeting
moduleId: 'custom-extensions',
register(env) {
env.registerInit({
deps: {
catalog: catalogProcessingExtensionPoint,
// ... and other dependencies as needed
},
async init({ catalog /* ..., other dependencies */ }) {
// Here you have the opportunity to interact with the extension
// point before the plugin itself gets instantiated
catalog.addEntityProvider(new MyEntityProvider()); // just an example
catalog.addProcessor(new MyProcessor()); // just an example
},
});
},
});

const backend = createBackend();
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.add(catalogModuleCustomExtensions);

This also requires that you have a dependency on the corresponding node package, if you didn't already have one.

# from the repository root
yarn --cwd packages/backend add @backstage/plugin-catalog-node

Here we've placed the module directly in the backend index file just to get going easily, but feel free to move it out to where it fits best. As you migrate your entire plugin flora to the new backend system, you will probably make more and more of these modules as "first class" things, living right next to the implementations that they represent, and being exported from there.

The Events Plugin

A basic installation of the events plugin looks as follows.

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-events-backend'));

If you have other customizations made to plugins/events.ts, such as adding custom subscribers, read on. Otherwise, you should be able to just delete that file at this point.

You will use the extension points mechanism to extend or tweak the functionality of the plugin. To do that, you'll make your own bespoke module which depends on the appropriate extension point and interacts with it.

packages/backend/src/index.ts
import { eventsServiceRef } from '@backstage/plugin-events-node';
import { eventsExtensionPoint } from '@backstage/plugin-events-node/alpha';
import { createBackendModule } from '@backstage/backend-plugin-api';

const eventsModuleCustomExtensions = createBackendModule({
pluginId: 'events', // name of the plugin that the module is targeting
moduleId: 'custom-extensions',
register(env) {
env.registerInit({
deps: {
events: eventsExtensionPoint,
// ... and other dependencies as needed
},
async init({ events /* ..., other dependencies */ }) {
// Here you have the opportunity to interact with the extension
// point before the plugin itself gets instantiated
events.addHttpPostIngress({
// ...
});
},
});
},
});

const otherPluginModuleCustomExtensions = createBackendModule({
pluginId: 'other-plugin', // name of the plugin that the module is targeting
moduleId: 'custom-extensions',
register(env) {
env.registerInit({
deps: {
events: eventsServiceRef,
// ... and other dependencies as needed
},
async init({ events /* ..., other dependencies */ }) {
// Here you have the opportunity to interact with the extension
// point before the plugin itself gets instantiated
},
});
},
});

const backend = createBackend();
backend.add(import('@backstage/plugin-events-backend'));
backend.add(eventsModuleCustomExtensions);
backend.add(otherPluginModuleCustomExtensions);

Here we've placed the module directly in the backend index file just to get going easily, but feel free to move it out to where it fits best. As you migrate your entire plugin flora to the new backend system, you will probably make more and more of these modules as "first class" things, living right next to the implementations that they represent, and being exported from there.

The Scaffolder Plugin

A basic installation of the scaffolder plugin looks as follows.

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-scaffolder-backend'));

With the new Backend System version of the Scaffolder plugin, any provider specific actions will need to be installed separately. For example - GitHub actions are now collected under the @backstage/plugin-scaffolder-backend-module-github package.

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-scaffolder-backend'));

backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));

And of course you'll need to install those separately as well.

# from the repository root
yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-github

You can find a list of the available modules under the plugins directory in the monorepo.

If you have other customizations made to plugins/scaffolder.ts, such as adding custom actions, read on. Otherwise, you should be able to just delete that file at this point.

You will use the extension points mechanism to extend or tweak the functionality of the plugin. To do that, you'll make your own bespoke module which depends on the appropriate extension point and interacts with it.

packages/backend/src/index.ts
import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha';
import { createBackendModule } from '@backstage/backend-plugin-api';

const scaffolderModuleCustomExtensions = createBackendModule({
pluginId: 'scaffolder', // name of the plugin that the module is targeting
moduleId: 'custom-extensions',
register(env) {
env.registerInit({
deps: {
scaffolder: scaffolderActionsExtensionPoint,
// ... and other dependencies as needed
},
async init({ scaffolder /* ..., other dependencies */ }) {
// Here you have the opportunity to interact with the extension
// point before the plugin itself gets instantiated
scaffolder.addActions(new MyAction()); // just an example
},
});
},
});

const backend = createBackend();
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.add(scaffolderModuleCustomExtensions);

This also requires that you have a dependency on the corresponding node package, if you didn't already have one.

# from the repository root
yarn --cwd packages/backend add @backstage/plugin-scaffolder-node

Here we've placed the module directly in the backend index file just to get going easily, but feel free to move it out to where it fits best. As you migrate your entire plugin flora to the new backend system, you will probably make more and more of these modules as "first class" things, living right next to the implementations that they represent, and being exported from there.

The Auth Plugin

A basic installation of the auth plugin with a Microsoft provider will look as follows.

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-microsoft-provider'));

An additional step you'll need to take is to add the resolvers to your configuration, here's an example:

auth:
environment: development
providers:
microsoft:
development:
clientId: ${AZURE_CLIENT_ID}
clientSecret: ${AZURE_CLIENT_SECRET}
tenantId: ${AZURE_TENANT_ID}
signIn:
resolvers:
- resolver: emailMatchingUserEntityProfileEmail
Note

The resolvers will be tried in order, but will only be skipped if they throw a NotFoundError.

Auth Plugin Modules and Their Resolvers

As you may have noticed in the above example you'll need to import the auth-backend and an auth-backend-module. The following sections outline each of them and their resolvers.

All of the following modules include the following common resolvers:

Atlassian

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-atlassian-provider'));

Additional resolvers:

GCP IAP (Google Identity-Aware Proxy)

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-gcp-iap-provider'));

Additional resolvers:

GitHub

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));

Additional resolvers:

GitLab

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-gitlab-provider'));

Additional resolvers:

Google

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-google-provider'));

Additional resolvers:

Microsoft

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-microsoft-provider'));

Additional resolvers:

oauth2

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-oauth2-provider'));

Additional resolvers:

oauth2 Proxy

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(
import('@backstage/plugin-auth-backend-module-oauth2-proxy-provider'),
);

Additional resolvers:

Okta

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-okta-provider'));

Additional resolvers:

Pinniped

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-pinniped-provider'));
VMware Cloud

Setup:

packages/backend/src/index.ts
const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(
import('@backstage/plugin-auth-backend-module-vmware-cloud-provider'),
);

Additional resolvers:

Custom Resolver

You may have a case where the common resolvers or the ones that are included with the auth module you use won't work for your needs. In this case you will need to create a custom resolver. Instead of the 2nd import for your auth provider module you would provide your own:

packages/backend/src/index.ts
export const authModuleGoogleProvider = createBackendModule({
pluginId: 'auth',
moduleId: 'googleProvider',
register(reg) {
reg.registerInit({
deps: { providers: authProvidersExtensionPoint },
async init({ providers }) {
providers.registerProvider({
providerId: 'google',
factory: createOAuthProviderFactory({
authenticator: googleAuthenticator,
async signInResolver(info, ctx) {
// custom resolver ...
},
}),
});
},
});
},
});

const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(authModuleGoogleProvider);

Using Legacy Providers

Not all authentication providers have been refactored to support the new backend system. If your authentication provider module is not available yet, you will need to import your backend auth plugin using the legacy helper:

packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import { legacyPlugin } from '@backstage/backend-common';
import { coreServices } from '@backstage/backend-plugin-api';

const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(legacyPlugin('auth', import('./plugins/auth')));

backend.start();

You can track the progress of the module migration efforts here.

The Search Plugin

A basic installation of the Search plugin will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-search-backend'));
Note

This will use the Lunr search engine which stores its index in memory.

Search Engines

The following sections outline how you can add other Search engines than the default lunr engine.

Postgres

An installation of the Search plugin using the Postgres search engine will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-search-backend'));
backend.add(import('@backstage/plugin-search-backend-module-pg'));
Elasticsearch

A basic installation of the Search plugin using the Elasticsearch search engine will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-search-backend'));
backend.add(import('@backstage/plugin-search-backend-module-elasticsearch'));

Search Collators

The following sections outline how you add search collators (input sources for the search indexing process).

Catalog

A basic installation of the Search plugin with the Catalog collator will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-search-backend'));
backend.add(import('@backstage/plugin-search-backend-module-catalog'));
TechDocs

A basic installation of the Search plugin with the TechDocs collator will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-search-backend'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs'));

The Permission Plugin

A basic installation of the Permission plugin will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-permission-backend'));
backend.add(
import('@backstage/plugin-permission-backend-module-allow-all-policy'),
);
Note

The above example includes a default allow-all policy. If that is not what you want, do not add the second line and instead investigate one of the options below.

Custom Permission Policy

In order to add your own permission policy you'll need to do the following:

import { createBackendModule } from '@backstage/backend-plugin-api';
import {
PolicyDecision,
AuthorizeResult,
} from '@backstage/plugin-permission-common';
import {
PermissionPolicy,
PolicyQuery,
PolicyQueryUser,
} from '@backstage/plugin-permission-node';
import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha';

class CustomPermissionPolicy implements PermissionPolicy {
async handle(
request: PolicyQuery,
user?: PolicyQueryUser,
): Promise<PolicyDecision> {
// TODO: Add code here that inspects the incoming request and user, and returns AuthorizeResult.ALLOW, AuthorizeResult.DENY, or AuthorizeResult.CONDITIONAL as needed. See the docs at https://backstage.io/docs/permissions/writing-a-policy for more information

return {
result: AuthorizeResult.ALLOW,
};
}
}

const customPermissionBackendModule = createBackendModule({
pluginId: 'permission',
moduleId: 'custom-policy',
register(reg) {
reg.registerInit({
deps: { policy: policyExtensionPoint },
async init({ policy }) {
policy.setPolicy(new CustomPermissionPolicy());
},
});
},
});

const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-permission-backend'));
backend.add(customPermissionBackendModule);

The TechDocs Plugin

A basic installation of the TechDocs plugin will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-techdocs-backend'));

The Kubernetes Plugin

A basic installation of the Kubernetes plugin will look as follows:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

backend.add(import('@backstage/plugin-kubernetes-backend'));

The Plugins in Backstage Repo

The vast majority of the backend plugins that currently live in the Backstage Repo have been migrated and their respective READMEs have details on how they should be installed using the New Backend System.

PackageRoleMigratedUses Alpha ExportLink to README
@backstage-community/plugin-adr-backendbackend-plugintrueREADME
@backstage-community/plugin-airbrake-backendbackend-plugintrueREADME
@backstage/plugin-app-backendbackend-plugintruetrueREADME
@backstage/plugin-auth-backendbackend-plugintrueREADME
@backstage/plugin-auth-backend-module-atlassian-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-aws-alb-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-gcp-iap-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-github-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-gitlab-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-google-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-guest-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-microsoft-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-oauth2-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-oauth2-proxy-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-oidc-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-okta-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-pinniped-providerbackend-plugin-moduletrueREADME
@backstage/plugin-auth-backend-module-vmware-cloud-providerbackend-plugin-moduletrueREADME
@backstage-community/plugin-azure-devops-backendbackend-plugintrueREADME
@backstage-community/plugin-azure-sites-backendbackend-plugintrueREADME
@backstage-community/plugin-badges-backendbackend-plugintrueREADME
@backstage-community/plugin-bazaar-backendbackend-plugintruetrueREADME
@backstage/plugin-catalog-backendbackend-plugintruetrueREADME
@backstage/plugin-catalog-backend-module-awsbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-azurebackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-backstage-openapibackend-plugin-moduletrueREADME
@backstage/plugin-catalog-backend-module-bitbucket-cloudbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-bitbucket-serverbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-gcpbackend-plugin-moduletrueREADME
@backstage/plugin-catalog-backend-module-gerritbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-githubbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-github-orgbackend-plugin-moduletrueREADME
@backstage/plugin-catalog-backend-module-gitlabbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-incremental-ingestionbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-ldapbackend-plugin-moduletrueREADME
@backstage/plugin-catalog-backend-module-msgraphbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-openapibackend-plugin-moduletrueREADME
@backstage/plugin-catalog-backend-module-puppetdbbackend-plugin-moduletruetrueREADME
@backstage/plugin-catalog-backend-module-scaffolder-entity-modelbackend-plugin-moduletrueREADME
@backstage/plugin-catalog-backend-module-unprocessedbackend-plugin-moduletrueREADME
@backstage-community/plugin-code-coverage-backendbackend-plugintrueREADME
@backstage/plugin-devtools-backendbackend-plugintrueREADME
@backstage-community/plugin-entity-feedback-backendbackend-plugintrueREADME
@backstage/plugin-events-backendbackend-plugintruetrueREADME
@backstage/plugin-events-backend-module-aws-sqsbackend-plugin-moduletruetrueREADME
@backstage/plugin-events-backend-module-azurebackend-plugin-moduletruetrueREADME
@backstage/plugin-events-backend-module-bitbucket-cloudbackend-plugin-moduletruetrueREADME
@backstage/plugin-events-backend-module-gerritbackend-plugin-moduletruetrueREADME
@backstage/plugin-events-backend-module-githubbackend-plugin-moduletruetrueREADME
@backstage/plugin-events-backend-module-gitlabbackend-plugin-moduletruetrueREADME
@internal/plugin-todo-list-backendbackend-plugintrueREADME
@backstage-community/plugin-explore-backendbackend-plugintrueREADME
@backstage-community/plugin-jenkins-backendbackend-plugintrueREADME
@backstage-community/plugin-kafka-backendbackend-plugintruetrueREADME
@backstage/plugin-kubernetes-backendbackend-plugintruetrueREADME
@backstage-community/plugin-lighthouse-backendbackend-plugintrueREADME
@backstage-community/plugin-linguist-backendbackend-plugintrueREADME
@backstage-community/plugin-nomad-backendbackend-plugintrueREADME
@backstage/plugin-notifications-backendbackend-plugintrueREADME
@backstage-community/plugin-periskop-backendbackend-plugintruetrueREADME
@backstage/plugin-permission-backendbackend-plugintruetrueREADME
@backstage/plugin-permission-backend-module-allow-all-policybackend-plugin-moduletrueREADME
@backstage-community/plugin-playlist-backendbackend-plugintrueREADME
@backstage/plugin-proxy-backendbackend-plugintruetrueREADME
@backstage-community/plugin-rollbar-backendbackend-pluginREADME
@backstage/plugin-scaffolder-backendbackend-plugintruetrueREADME
@backstage/plugin-scaffolder-backend-module-azurebackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-bitbucketbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-bitbucket-cloudbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-bitbucket-serverbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-confluence-to-markdownbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-cookiecutterbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-gerritbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-giteabackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-githubbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-gitlabbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-railsbackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-sentrybackend-plugin-moduletrueREADME
@backstage/plugin-scaffolder-backend-module-yeomanbackend-plugin-moduletrueREADME
@backstage/plugin-search-backendbackend-plugintruetrueREADME
@backstage/plugin-search-backend-module-catalogbackend-plugin-moduletruetrueREADME
@backstage/plugin-search-backend-module-elasticsearchbackend-plugin-moduletruetrueREADME
@backstage/plugin-search-backend-module-explorebackend-plugin-moduletruetrueREADME
@backstage/plugin-search-backend-module-pgbackend-plugin-moduletruetrueREADME
@backstage/plugin-search-backend-module-stack-overflow-collatorbackend-plugin-moduletrueREADME
@backstage/plugin-search-backend-module-techdocsbackend-plugin-moduletruetrueREADME
@backstage/plugin-signals-backendbackend-plugintrueREADME
@backstage-community/plugin-sonarqube-backendbackend-plugintrueREADME
@backstage-community/plugin-stack-overflow-backendbackend-pluginREADME
@backstage-community/plugin-tech-insights-backendbackend-plugintrueREADME
@backstage-community/plugin-tech-insights-backend-module-jsonfcbackend-plugin-moduletrueREADME
@backstage/plugin-techdocs-backendbackend-plugintruetrueREADME
@backstage-community/plugin-todo-backendbackend-plugintrueREADME
@backstage/plugin-user-settings-backendbackend-plugintruetrueREADME
@backstage-community/plugin-vault-backendbackend-plugintrueREADME