Skip to main content

Customization

By default, the Portal Controller generates entities straight into the writer, no customization needed. If you want to shape the entities a Promise (and its Resources) publish to a portal, you can bind it to a PortalCustomization.

A PortalCustomization declares containers that run between generate and writer and edit the generated documents in place:

generate → configure (this profile's containers, in order) → writer

The lane runs any container image you give it, so you can use your own tooling, an off-the-shelf image, or the helper described below.

tip

If you would rather not write a container, Portal Patch is a helper image we ship that applies declarative recipes in this lane, covering setting owners and tags, removing form fields, and reshaping Cortex workflows. It is entirely optional, and you can mix it with your own containers in the same pipeline.

Creating a PortalCustomization

PortalCustomization (platform.syntasso.io/v1alpha1) is a cluster-scoped resource:

apiVersion: platform.syntasso.io/v1alpha1
kind: PortalCustomization
metadata:
name: backstage-add-team-owner
spec:
portalType: backstage # required: the portal type this profile applies to
promise:
configure:
containers:
- name: portal-patch
image: ghcr.io/syntasso/portal-patch:0.7.0
args:
- |
target:
kind: Component
patch:
spec:
owner: platform-team
resource:
configure:
containers:
- name: portal-patch
image: ghcr.io/syntasso/portal-patch:0.7.0
args:
- |
target:
kind: Component
patch:
metadata:
tags: [tier-1]
  • spec.portalType (required) is the portal type this profile applies to, for example backstage. It is never a specific portal, credential, or URL, those live on the SKEIntegration.
  • spec.promise.configure runs for the Promise-level sync.
  • spec.resource.configure runs once per bound Resource request.

Either lane may be omitted. A profile with only promise leaves Resource syncs untouched, and the other way round.

The configure lane is a Kratix pipeline

Each lane is a full Kratix PipelineSpec, not just a list of containers. Alongside containers[], which take the usual image, args, command, env, envFrom, volumeMounts, resources and securityContext, you can set volumes, imagePullSecrets, rbac, jobOptions, nodeSelector and tolerations.

Containers run in the order listed. They read the parent object from /kratix/input/object.yaml and edit the generated documents in /kratix/output in place.

warning

Do not write /kratix/metadata/destination-selectors.yaml from a configure container unless you are deliberately redirecting output. The generate stage writes one entry for its output directory, and duplicate entries for the same directory are rejected by the writer.

Configure containers run for the configure action only. The delete pipeline is writer-only, so a configure container never runs when a Promise or Resource is deleted.

Binding a Promise to a PortalCustomization

Label the Promise with the profile's name:

metadata:
labels:
kratix.io/portal-customization: backstage-add-team-owner

The label alone is enough to opt in. The portal type is derived from the profile's own spec.portalType, so you do not also need a kratix.io/portal-<type> label.

important

A Promise may be bound to one PortalCustomization only. If more than one kratix.io/portal-customization label is present, the Promise reports PortalBindingReady: False with reason AmbiguousCustomization and does not sync.

To apply several sets of changes, list multiple containers in one profile rather than binding multiple profiles.

If you have more than one portal of the profile's type, use an instance suffix to say which portal the Promise should sync to. The suffix names the portal, not a second profile:

metadata:
labels:
kratix.io/portal-customization.staging: backstage-add-team-owner

Worked example: setting owner and tags without Portal Patch

Where a recipe will not do, any container that can read and write YAML works:

apiVersion: platform.syntasso.io/v1alpha1
kind: PortalCustomization
metadata:
name: cicd-backstage-customization
spec:
portalType: backstage
resource:
configure:
containers:
- name: set-owner-and-tags
image: ghcr.io/syntasso/kratix-pipeline-utility:v0.0.1
command: ["sh", "-c"]
args:
- |
set -e
file=/kratix/output/catalog-info.yaml
yq -i '.spec.owner = "platform-team"' "$file"
yq -i '.metadata.tags = (.metadata.tags // []) + ["tier-1", "managed-by-kratix"]' "$file"

Bind the Promise to it:

metadata:
labels:
kratix.io/portal-customization: cicd-backstage-customization

Status

A PortalCustomization reports a single Accepted condition. This reflects whether the resource itself is structurally valid, Validated when it is and PortalTypeMissing when spec.portalType is absent. It does not reflect whether any particular Promise's binding to it resolved.

Whether a Promise's binding resolved is reported on the Promise, via the PortalBindingReady condition described in Installation.

tip

PortalCustomization resources are cluster-wide. Give them unique, descriptive names.

Examples