Skip to main content

Generate Backstage entities

Configure the Portal Controller to generate a Component and Template for a Promise. This guide ends with catalog-info.yaml in your chosen storage. Backstage discovery comes next.

Prerequisites​

  • SKE and, for operator-managed installation, the SKE Operator meeting the controller requirements.
  • kubectl access to the platform cluster.
  • An installed Promise with spec.api. The commands below use postgresql; replace it with your Promise name.
  • An existing Git repository or S3-compatible bucket, accessible from the platform and Backstage.
  • Credentials to write catalog files. Backstage needs its own read access.

The integration creates Kubernetes storage objects, not the repository or bucket itself. Keep catalog files separate from manifests your GitOps agent applies.

Install or reuse the controller​

Check for an existing installation:

kubectl get deployment ske-portal-controller -n kratix-platform-system

With the SKE Operator, also check kubectl get skeintegrations for the integration name.

If absent, follow Install and upgrade, then return here. Keep its SKEIntegration manifest: the next step adds a portal to it.

Without the SKE Operator, use the same portals entry in ske-portal-config, not an SKEIntegration. Follow the standalone apply instructions below.

Configure catalog storage​

Choose one ownership model:

ConfigurationIntegration ownsYou manage
Define Destination and state storeBoth Kubernetes objectsRepository or bucket, credentials
Define Destination, reference state storeDestinationState store, backing storage, credentials
Reference DestinationNeitherDestination, state store, backing storage, credentials
Preserve existing portal entries

Edit the existing integration; do not create another. Preserve its name, version, other portal entries and deployment settings. Removing an entry deletes its managed storage objects.

The complete example below uses the integration name portal-controller and portal name prod. For an existing installation, add a new portals entry to your manifest.

Define Destination and state store​

Create the credential Secret in kratix-platform-system. Save this as catalog-credentials.yaml, replacing both placeholders. Use a token with write access to the catalog repository.

Protect credentials

Do not commit the populated Secret manifest. Use your organisation's secret-management workflow.

apiVersion: v1
kind: Secret
metadata:
name: backstage-catalog-repo
namespace: kratix-platform-system
type: Opaque
stringData:
username: "<GITHUB_USERNAME>"
password: "<GITHUB_TOKEN>"
kubectl apply -f catalog-credentials.yaml

Save the integration as portal-controller.yaml. Replace <GITHUB_ORG> and <CATALOG_REPOSITORY>:

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

Setup creates a Destination and GitStateStore named portal-backstage-prod. The catalog prefix is gitops/backstage/prod: state store path, then Destination path. Other Git authentication options are in the GitStateStore reference.

Use a bucket instead of Git

For a bucket, replace only config.stateStore in the integration above with this fragment. Create the bucket first; replace <BUCKET_NAME> and use its endpoint.

stateStore:
kind: BucketStateStore
spec:
bucketName: "<BUCKET_NAME>"
endpoint: s3.eu-west-2.amazonaws.com
path: gitops
authMethod: accessKey
secretRef:
name: backstage-catalog-bucket
namespace: kratix-platform-system

Use this Secret instead of the Git credential Secret:

apiVersion: v1
kind: Secret
metadata:
name: backstage-catalog-bucket
namespace: kratix-platform-system
type: Opaque
stringData:
accessKeyID: "<ACCESS_KEY_ID>"
secretAccessKey: "<SECRET_ACCESS_KEY>"

Save it as catalog-credentials.yaml and apply it before the integration. The catalog prefix remains gitops/backstage/prod. See BucketStateStore for other authentication options. Backstage needs a discovery provider that supports your bucket service.

Reuse a state store​

Replace only the config block in the complete integration above with this fragment. Replace <STATE_STORE_NAME>; set kind to its actual kind.

config:
destination:
spec:
path: backstage/prod
stateStore:
kind: GitStateStore
name: "<STATE_STORE_NAME>"

Setup creates the Destination portal-backstage-prod, leaving the existing state store under your management. Its path precedes backstage/prod in the output prefix.

Reuse a Destination​

Replace only the config block in the complete integration above with this fragment:

config:
destination:
name: "<DESTINATION_NAME>"

Setup creates neither object. The Destination must already exist and reference a working state store. Its own path and the state store's path determine the output prefix. Do not include stateStore here.

The controller routes using the Destination's labels, not its name. Give it a non-empty label set that selects only that Destination. For example, add a unique catalog label:

kubectl label destination <DESTINATION_NAME> backstage-catalog=prod
kubectl get destinations -l backstage-catalog=prod

The second command must return only your chosen Destination. Preserve the label in its source manifest if GitOps manages it. Set it before binding the Promise. Without labels, routing falls back to environment: backstage and can select different storage.

Apply the configuration​

With the SKE Operator, apply your edited integration:

kubectl apply -f portal-controller.yaml

Without the operator, copy your selected portal entry from spec.portals into the data.connections.yaml list in your existing ConfigMap manifest. There is no portals: wrapper inside that value. Preserve other entries and save it as portal-config.yaml. See the complete ConfigMap shape.

kubectl apply -f portal-config.yaml

For either installation, inspect setup events:

kubectl describe configmap ske-portal-config -n kratix-platform-system

Check for PortalSetupFailed events. For an owned Destination, verify it exists:

kubectl get destination portal-backstage-prod -o yaml

For a reused Destination, inspect its actual name instead.

Label your Promise​

Bind the Promise to the prod instance:

kubectl label promise postgresql kratix.io/portal-backstage.prod=true

If the Promise already has a Backstage binding, update that label in its source manifest instead of adding a second binding. Keep the label in Git if GitOps manages the Promise.

Check its conditions:

kubectl get promise postgresql -o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\n"}{end}'

Look for PortalBindingReady=True and BackstagePortalSynced=True (EntitySynced). These confirm binding and the sync Job; verify storage separately below.

Verify generated files​

Inspect your repository's configured branch or list the bucket objects. For the owned examples, find catalog-info.yaml under:

gitops/backstage/prod/dependencies/postgresql/portal-backstage-gen/<id>/catalog-info.yaml

The file contains two YAML documents: a Promise Component and its scaffolder Template. The Template's form comes from the Promise API. Do not edit generated files directly; the next sync can replace them.

Resource requests produce their own Components under:

gitops/backstage/prod/resources/kratix-platform-system/postgresql/<resource-name>/portal-backstage-gen/<id>/catalog-info.yaml

The namespace segment is the Work namespace, not the request namespace. With reused storage, substitute its actual prefix. Resource files appear after requests exist; they are not required for this first checkpoint.

Record the repository or bucket, branch where applicable, and prefix. Use those same coordinates for Backstage discovery in the next guide.

Troubleshooting​

SymptomCheck
Destination absentPortalSetupFailed events on ske-portal-config; storage name/spec choices and credential references
Binding not readyInstance name and label; binding conditions
Sync failsSync Job diagnostics; Promise API and image access
Sync succeeds but files absentDestination readiness, state store credentials, branch and combined path
Files exist but catalog emptyBackstage discovery provider and its independent read credentials

Next steps​

Configure Backstage to discover these files and install the SKE plugins.