Skip to main content

Backstage

A Backstage portal publishes Promises and Resources as Backstage catalog entities. Unlike Cortex, the Portal Controller never calls Backstage. It writes the generated catalog-info.yaml files to a Kratix Destination, which is a Git repository or bucket, and Backstage discovers them through its own catalog providers.

That means a Backstage portal needs no url and no secretRef. What it does need is somewhere to write, which is what config describes.

Configuration

A Backstage portal takes exactly three config keys, all optional:

KeyMeaningDefault
modeThe delivery mode, push or pull-requestpush
destinationWhere entities are written, either adopting an existing Destination or defining onenone
stateStoreThe state store backing an owned Destinationnone

Owning a Destination

The simplest setup is to let the integration create and own its Destination and state store. Give destination.spec.path, which is where inside the store the entities live, and a stateStore block describing the store to create:

apiVersion: platform.syntasso.io/v1alpha1
kind: SKEIntegration
metadata:
name: portal-controller
spec:
type: portal-controller
version: v0.2.0
portals:
- name: prod
type: backstage
config:
mode: push
destination:
spec:
path: gitops/backstage/prod
stateStore:
kind: GitStateStore
spec:
url: https://github.com/acme/platform-catalog.git
branch: main
authMethod: basicAuth
secretRef:
name: backstage-catalog-repo
namespace: kratix-platform-system

The integration creates both objects, named portal-backstage-prod after the portal type and name, and owns them. Removing the portal removes them.

stateStore.kind is required and must be GitStateStore or BucketStateStore. There is no default, because the two are distinct kinds and guessing would silently target the wrong one.

Using a bucket instead of Git

Entities publish equally well from a bucket. Backstage must be able to read it.

config:
destination:
spec:
path: gitops/backstage/prod
stateStore:
kind: BucketStateStore
spec:
bucketName: kratix
endpoint: s3.eu-west-1.amazonaws.com
authMethod: accessKey
secretRef:
name: backstage-catalog-bucket
namespace: kratix-platform-system

You must create the credential Secret the state store references yourself. The spec block is passed through to the Kratix state store unchanged, so it takes the same fields as any GitStateStore or BucketStateStore.

Referencing an existing state store

If you already manage state stores, name one instead of defining it. The integration still owns the Destination, but not the store:

config:
destination:
spec:
path: gitops/backstage/prod
stateStore:
kind: BucketStateStore
name: default

Adopting an existing Destination

If you already manage Destinations, name one. Nothing is created:

config:
mode: push
destination:
name: my-destination

An adopted Destination carries its own state store, so stateStore is ignored here. Set the Destination's own path on the Destination itself.

Configuration errors

destination and stateStore are validated when the portal's setup Job runs, and a failure is reported as a PortalSetupFailed event on the SKEIntegration:

MessageCause
config.destination must set exactly one of name or spec, got bothAdopting and owning at once
config.destination must set exactly one of name or spec, got neitherAn empty destination block
config.destination.spec.path is requiredOwning a Destination without a path
config.destination.spec requires a config.stateStoreOwning a Destination with no store to back it
config.stateStore.kind is required and must be "GitStateStore" or "BucketStateStore"Missing or misspelled kind
config.stateStore must set exactly one of name or specReferencing and defining at once
has invalid config.modeA mode other than push or pull-request

What gets written where

Entities are written under the state store's path, then the Destination's path:

<stateStore path>/<destination path>/dependencies/<promise>/portal-backstage-gen/<id>/catalog-info.yaml
<stateStore path>/<destination path>/resources/kratix-platform-system/<promise>/<name>/portal-backstage-gen/<id>/catalog-info.yaml
  • The dependencies tree holds Promise-level entities. Each file contains two documents, the Promise Component and the scaffolder Template developers use to request a Resource.
  • The resources tree holds one Component per Resource request.

You need both paths when configuring Backstage's catalog discovery, which is covered in Configure Backstage.

note

The namespace segment in the resources path is always kratix-platform-system, regardless of where the Resource request lives. It is the namespace of the Kratix Work, not of the request, and it is not configurable.

Next steps