Harbor logo

Backstage Harbor Plugin

Created by @BESTSELLER

Harbor is an open source container registry from the CNCF. It secures artifacts with role based access control, scans images for vulnerabilities, and supports replication across registries. Teams use it to manage images for Kubernetes and Docker at scale.

The Harbor Backstage plugin brings that data into your developer portal. It adds a page and a small widget to each service so you can see repositories that belong to the component. You can review tags and digests, confirm the latest push, and check scan status when scans run in Harbor. This keeps build, security, and release context in one place for the team.Typical use cases include verifying what is running before a rollout, spotting images that need rebuilds after a scan, and giving service owners a clear view without opening the registry UI. Platform and security teams get the same picture with less switching.

One company using the plugin is Digitalist Open Cloud. They maintain a fork to keep it current with newer Backstage releases.

Harbor in Backstage

Installation Instructions

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

Install the frontend package

Copy
yarn --cwd packages/app add @bestsellerit/backstage-plugin-harbor

Register the plugin in your app

Add the plugin export.

Copy
// packages/app/src/plugins.ts
export { plugin as harbor } from '@bestsellerit/backstage-plugin-harbor'

Add the Harbor page to the service entity page

Import the components. Add a route so users can open the Harbor page from the entity sidebar.

Copy
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react'
import { EntityLayout, EntityPageLayout } from '@backstage/plugin-catalog'
import {
  HarborPage,
  HarborWidget,
  isHarborAvailable,
} from '@bestsellerit/backstage-plugin-harbor'

const serviceEntityPage = (
  <EntityPageLayout>
    {/* other routes */}
    <EntityLayout.Route path="/harbor" title="Harbor" if={isHarborAvailable}>
      <HarborPage />
    </EntityLayout.Route>
  </EntityPageLayout>
)

Show the Harbor widget on the overview

Place the widget in the overview content.

Copy
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react'
import Grid from '@material-ui/core/Grid'
import { EntitySwitch } from '@backstage/plugin-catalog'
import {
  HarborWidget,
  isHarborAvailable,
} from '@bestsellerit/backstage-plugin-harbor'

const overviewContent = (
  <Grid container spacing={6} alignItems="stretch">
    {/* other content */}
    <EntitySwitch>
      <EntitySwitch.Case if={isHarborAvailable}>
        <Grid item>
          <HarborWidget />
        </Grid>
      </EntitySwitch.Case>
    </EntitySwitch>
  </Grid>
)

Install the backend for the legacy backend system

Add the backend package to the backend workspace.

Copy
yarn --cwd packages/backend add @container-registry/backstage-plugin-harbor-backend

Create the router factory.

Copy
// packages/backend/src/plugins/harbor.ts
import { createRouter } from '@container-registry/backstage-plugin-harbor-backend'
import { PluginEnvironment } from '../types'

export default async function createPlugin(env: PluginEnvironment) {
  return await createRouter({
    logger: env.logger,
    config: env.config,
    discovery: env.discovery,
    tokenManager: env.tokenManager,
  })
}

Wire the router into the backend.

Copy
// packages/backend/src/index.ts
import harbor from './plugins/harbor'

// inside the main function after creating the env
const harborEnv = useHotMemoize(module, () => createEnv('harbor'))

// inside the router setup
apiRouter.use('/harbor', await harbor(harborEnv))

Add the required config. Fill in the values for your Harbor instance and credentials. Keep the same keys so the plugin can read them.

Copy
# app-config.yaml
harbor:
  baseUrl: https://harbor.example.com
  username: ${HARBOR_USERNAME} # or a robot account
  password: ${HARBOR_PASSWORD} # or a robot token
  # tlsInsecure: false
  # any other keys supported by the backend plugin

Export secrets as env vars in your backend process or set them in your secrets store.

Copy
export HARBOR_USERNAME=robot$backstage
export HARBOR_PASSWORD=your_token_here

Install the backend for the new backend system

Add the backend package.

Copy
yarn --cwd packages/backend add @container-registry/backstage-plugin-harbor-backend

Register the backend plugin feature.

Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults'
import { harborPlugin } from '@container-registry/backstage-plugin-harbor-backend'

const backend = createBackend()

backend.add(harborPlugin())

backend.start()

Use the same config keys in app config.

Copy
# app-config.yaml
harbor:
  baseUrl: https://harbor.example.com
  username: ${HARBOR_USERNAME}
  password: ${HARBOR_PASSWORD}

Add entity metadata

Make sure your entities contain the data that the plugin checks through isHarborAvailable. Add the needed annotations in the entity yaml.

Copy
# catalog-info.yaml
metadata:
  name: my-service
  annotations:
    # add Harbor related annotations here
    # example keys depend on your setup
spec:
  type: service
  owner: team-a
  lifecycle: production

Things to Know

The Harbor Dashboard appears as a new tab on the catalog entity page:

Harbor dashboard close-up

A Harbor widget is also available to display a summary of vulnerabilities in your component’s Docker image. This widget can be added to any tab on the entity page:

Harbor vulnerability widget card

The plugin will only display information about images in Harbor if a vulnerability scan has been run on the image. You can enable automatic scanning on image push in Harbor project settings:

Harbor automate image scan setting

Authentication

The plugin connects to Harbor with a username and password that is stored in app-config.yaml. Credentials can be defined directly in the YAML file or passed in via environment variables like this:

Copy
      harbor:
        baseUrl: https://demo.goharbor.io
        username: 
          $env: HARBOR_USERNAME
        password:
          $env: HARBOR_PASSWORD

Consider creating a dedicated Harbor user for the connection from Backstage.

Don’t have Harbor setup yet?

A demo of Harbor is available at demo.goharbor.io. The demo allows you to create an account and push images. All accounts and images are automatically purged on a regular basis. This is a great and easy way to experiment with Harbor in Backstage.

Changelog

The Harbor plugin has not seen any significant changes since a year ago.

Set up Backstage in minutes with Roadie