Spotify’s Backstage is a powerful tool, offering developers a software catalog, templates, a wealth of plugins, and a bunch of other features that help engineering teams organize services and infra. Backstage is even more powerful when equipped with the Cortex plugin, providing unparalleled visibility into the performance of your services, all in one place.
In this guide, we’ll take a look at how to set up Cortex in Backstage. If you aren’t already up and running with Backstage, make sure you’ve read our guide to getting started.
Cortex has two different plugins for Backstage: one for the frontend, and one for the backend.
Frontend plugin
The frontend plugin is designed to mirror Cortex within Backstage, so the layout and information will be familiar. This plugin is responsible for making API calls to Cortex, fetching information, and displaying it within Backstage.
Step 1
To set up the frontend, first add the plugin within the packages/app directory of your Backstage instance:
cd packages/app
yarn add @cortexapps/backstage-plugin
This will add the plugin as a dependency within the package’s JSON, and will pull the plugin’s library into Backstage, which will allow it to be hooked up with Cortex.
Step 2
Navigate to packages/app/src, and create a new file called plugins.ts. Add the following to plugins.ts:
export { cortexPlugin } from ‘@cortexapps/backstage-plugin’;
Step 3
Next, navigate to packages/app/src/App.tsx — this file dictates what will be displayed on the frontend. First, import the file from the previous step:
And then add a new route, so that you can automatically go to the Cortex page by appending /cortex to your Backstage URL:
<Route path=“/cortex” element={<CortexPage /> } />
Step 4
Because you need an API key for Backstage to communicate with Cortex, you’ll also need to update the app-config.yaml file with a proxy config:
‘/cortex’:
target: ${CORTEX_BACKEND_HOST_URL}
headers:
Authorization: ${CORTEX_TOKEN}
Cortex uses two different environment variables to avoid security vulnerabilities. If you run into issues during this stage, confirm that Backstage is pointing to the right URL for Cortex, and make sure that your token has a Bearer prefix.
This step is vital, since this is what allows Backstage to communicate with Cortex, and allows the plugin to fetch information for display.
Step 5
The steps above will set up the main Cortex page within Backstage. To add a Cortex tab for each entity, navigate to packages/app/src/components/catalog/EntityPage.tsx and import EntityCortexContent:
import { EntityCortexContent } from ‘@cortexapps/backstage-plugin’;
Then, update the serviceEntityPage with the following block to add the Cortex tab:
<EntityLayout.Route path=“/cortex” title=“Cortex”>
<EntityCortexContent />
</EntityLayout.Route>;
Within every service in your Backstage catalog, you should now see the Cortex tab appear, along with Scorecard information about that service:
At this point, we’ve completed all the necessary steps for setting up the frontend plugin. There are a few additional features you can add by following the same basic steps.
Optional: Sidebar button
To add a sidebar button for Cortex, navigate to packages/app/src/components/Root/Root.tsx and add the following:
import { CortexIcon } from ‘@cortexapps/backstage-plugin’;
<SidebarItem icon={CortexIcon} to=“cortex” text=“Cortex” />;
Optional: Widgets
You can add a widget that will display Scorecard information for specific components. Within EntityPage.tsx, add the following:
import { CortexScorecardWidget } from ‘@cortexapps/backstage-plugin’;
<Grid item md={4} xs={12}>
<CortexScorecardWidget />
</Grid>
You can even add group widgets, which will show Initiatives and Scorecards for components that are owned by a team. To add initiatives, include the following within EntityPage.tsx:
import { CortexGroupActionItemsWidget } from ‘@cortexapps/backstage-plugin’;
<Grid item md={4} xs={12}>
<CortexGroupActionItemsWidget />
</Grid>
To do the same for group Scorecards, include the following within EntityPage.tsx:
import { SystemCortexContent } from ‘@cortexapps/backstage-plugin’;
<EntityLayout.Route path=“/cortex” title=“Cortex”>
<SystemCortexContent />
</EntityLayout.Route>
Sync entities
The first time you add the frontend plugin, you may not see Scorecards immediately, because Backstage and Cortex haven’t synced yet. To manually sync the apps, navigate to Cortex in the sidebar, open Settings, and click the refresh icon:
Depending on the number of entities you have, this may take a while — that’s where the backend plugin comes in.
Backend plugin
The backend plugin is responsible for making sure Backstage and Cortex stay up to date. Both platforms use YAML files to represent different services and entities. The backend plugin pushes the list of all the Backstage YAMLs into Cortex, where they’re converted to Cortex’s internal YAML, and then synced with Backstage. While you have the option to manually sync Backstage and Cortex, this plugin will take care of that automatically.
Step 1
To set up the backend plugin, you’ll first need to add the plugin library within the packages/backend directory of your Backstage instance:
cd packages/backend
yarn add @cortexapps/backstage-backend-plugin
Step 2
Within Backstage, navigate to packages/backend/src/plugins, and create a new file named Cortex.ts. Within Cortex.ts, copy and paste the following code:
import { PluginEnvironment } from ‘../types’;
import { createRouter } from ‘@cortexapps/backstage-backend-plugin’;
export default async function createPlugin(env: PluginEnvironment) {
return await createRouter({
discoveryAPI: env.discovery,
logger: env.logger,
cronSchedule: env.config.getOptionalString(‘cortex.backend.cron’) ?? ‘0 3,7,11,15,19,23 *’
});
}
The cron expression will determine how frequently Backstage and Cortex sync. In this example, the two will sync every day, every four hours, starting at 3 a.m. This is fully configurable, so you can easily set the exact sync schedule you want, down to the second.
Step 3
Next, navigate to packages/backend/src, and within the index.ts file, import the file you just created:
import cortex from ‘./plugins/cortex’;
Then, add these lines to hook everything together:
const cortexEnv = useHotMemoize(module, () => createEnv(‘cortex’));
...
apiRouter.use(‘/cortex’, await cortex(cortexEnv));
Proxy config
Because we already added a proxy config when we set up the frontend plugin, it will already be in place for us. But, if you’re setting up the backend before the frontend, you’ll need to add the following in the app-config.yaml file:
‘/cortex’:
target: ${CORTEX_BACKEND_HOST_URL}
headers:
Authorization: ${CORTEX_TOKEN}
Using Backstage and Cortex together
Once the backend is set up, you’ll need to restart Backstage in order for the changes to be picked up. After that restart, you’ll see a one-to-one mapping between the two platforms. As you can see below, the number of components/services is the same, the services listed are the same, and the correct owners are reflected:
Backstage catalog view
Cortex catalog view
Note that if you’ve been using Backstage and you’re starting up Cortex for the first time, your source of truth lives in Backstage, so the first time you spin up Cortex it may take some time for your catalog to load. Once the catalog does load, you can confirm that the mapping is correct and that your setup is complete.