Skip to main content

Portal configuration and bindings

Configure available portal instances, then select them with binding labels. For setup, follow Backstage or Cortex.

Configuration resource​

The SKE Operator manages the controller through one cluster-scoped SKEIntegration of type portal-controller. This example publishes to an existing Destination named backstage-catalogue, which must already have a working Git or bucket StateStore:

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:
name: backstage-catalogue

Use one integration per platform. Add instances to its portals list; portals: [] installs without publishing. See the SKE Operator API for deployment settings.

Portal fields​

Field in spec.portals[]TypeRequirement and behaviour
namestringRequired; unique within the type. Used by instance-suffixed labels.
typestringRequired; backstage or cortex for the integrations documented here.
urlstringRequired for Cortex's API base URL; unused by Backstage.
secretRef.namestringRequired for Cortex; names a Secret in kratix-platform-system. Unused by Backstage.
configobjectPortal-specific settings; values can be strings or structured objects.
adapterConfigobjectOptional image, pull-secret, generate and exporter overrides; see Adapter reference.

Backstage settings​

Key in configBehaviour
destination.nameAdopt an existing Destination and its StateStore. Do not also set destination.spec.
destination.spec.pathCreate a controller-owned Destination at this path. Requires stateStore.
stateStore.kindGitStateStore or BucketStateStore; required when defining a Destination.
stateStore.nameReference an existing StateStore. Mutually exclusive with stateStore.spec.
stateStore.specCreate a StateStore from this spec. Mutually exclusive with stateStore.name.
modepush (default) or pull-request. Selects request approval, not catalogue publication or the backend's ske.mode. Pull requests are GitHub-only.
requestNamespaceNamespace policy for generated requests; defaults to from: form. See Request namespace.

When destination is absent, output selects existing Destinations labelled environment: backstage; you must provide matching storage yourself. When destination.name is set, stateStore is ignored because the Destination already specifies its store. The Backstage guide includes owned, referenced and adopted storage examples. Catalogue files are written to storage in both request delivery modes.

Cortex settings​

Key in configBehaviour
repositoryNameRequired; repository receiving Resource request manifests.
providerRequired; github or gitlab.
integrationAliasRequired; alias of the Git integration configured in Cortex.
branchRequest branch; defaults to main.
pathRequest directory; defaults to kratix-resource-requests.

Requests use <path>/<promise>/<namespace>-<name>.yaml. This repository holds requests, not published Cortex entity definitions. See the Cortex guide for API permissions and GitOps prerequisites.

Configuration ownership​

The controller reads ske-portal-config in kratix-platform-system, under the connections.yaml key. The value is a YAML list with the same entry fields as SKEIntegration.spec.portals.

  • Operator-managed: edit SKEIntegration.spec.portals. The operator writes the ConfigMap; manual edits are not the source of truth.
  • Standalone: maintain the ConfigMap yourself. See standalone installation.

For a controller installed without the operator, the existing-Destination example becomes:

apiVersion: v1
kind: ConfigMap
metadata:
name: ske-portal-config
namespace: kratix-platform-system
data:
connections.yaml: |
- name: prod
type: backstage
config:
destination:
name: backstage-catalogue

Portal changes rerun that instance's setup. Shared Job settings can rerun setup across instances. Rotating a Secret's contents does not guarantee a setup rerun.

Removing configuration performs cleanup

Removing an instance removes its controller-managed setup workloads and owned objects. Deleting this ConfigMap is not a way to pause synchronisation. Distinguish adopted storage from controller-owned storage when planning a change.

Credential Secret handling​

Create Cortex's Secret in kratix-platform-system before configuring its portal, with the credential under token. This example contains a placeholder, not a usable token:

apiVersion: v1
kind: Secret
metadata:
name: cortex-token
namespace: kratix-platform-system
type: Opaque
stringData:
token: <YOUR_CORTEX_API_TOKEN>

Manage credentials outside Git. The operator checks Secret existence, not token validity. Cortex API-facing workloads use Secret key references; generate/configure receive no token injection. Backstage publication needs no API token.

Binding labels​

These fragments show only metadata.labels, not complete Promises or requests:

metadata:
labels:
kratix.io/portal-backstage: "true"

Unsuffixed labels require one instance of the type. Otherwise, name an instance:

metadata:
labels:
kratix.io/portal-backstage.prod: "true"

On a Promise, the label's presence opts in; even the string "false" opts in. Removing the label opts out and can trigger cleanup of previously published output.

Opting a single Resource out​

Requests inherit their Promise's binding. Set the same key, including any instance suffix, to the string "false" on a request to skip it:

metadata:
labels:
kratix.io/portal-backstage.prod: "false"

Skipped requests generate no entity or portal condition. Opt-out skips sync; it is not a cleanup policy.

Binding a PortalCustomization​

kratix.io/portal-customization: <NAME> opts in using the resource's spec.portalType. kratix.io/portal-customization.prod: <NAME> selects instance prod. A Promise can reference one PortalCustomization. The suffix names the instance. See PortalCustomization for its schema.

Binding status​

The Promise's PortalBindingReady condition reports label resolution:

ReasonMeaning
BindingResolvedThe binding resolved to one instance.
AmbiguousPortalInstanceMore than one instance of the type exists and none was named.
PortalConnectionNotFoundNo configured instance matches the binding.
PortalConfigNotFoundThe configuration ConfigMap is missing.
CustomizationNotFoundThe named PortalCustomization does not exist.
AmbiguousCustomizationMore than one customisation label is present.

Invalid connections.yaml is different from a missing ConfigMap: the controller records a PortalConfigInvalid warning event on the ConfigMap.

Each sync reports BackstagePortalSynced or CortexPortalSynced:

ReasonMeaning
EntitySyncedThe sync pipeline completed publication. Check ingestion and provisioning separately.
SyncInProgressThe sync Job is running.
GenerateOrWriteFailedThe sync Job failed.
PromiseHasNoAPIThe Promise has no spec.api to render.

On success, kratix.io/portal-<type>-entity-url on the Promise points to its entity.