Delivery modes
For the complete setup and request lifecycle, see Send resource requests.
A Backstage portal's config.mode decides what happens when a developer fills in a generated
Template and clicks submit.
| Mode | What submitting does | Use when |
|---|---|---|
push (default) | The request is applied to the platform immediately | Self-service should be instant |
pull-request | The request is raised as a pull request for review | A human must approve before anything is created |
The mode is set per portal, and applies to every Promise routed through it:
portals:
- name: prod
type: backstage
config:
mode: pull-request
config.mode is not the same setting as the Backstage plugin's backend-wide ske.mode. This one
decides whether a request goes through review. ske.mode decides how the backend writes an
approved request, and is covered in Send resource requests.
Push mode
The generated Template asks for the resource name, namespace, and the Promise's own spec fields,
then runs two steps. For a Promise whose kind is Postgresql they are named:
- Submitting your Postgresql request, applying the request to the platform.
- Registering your Postgresql request in the catalog, telling the Backstage backend a request has been submitted.
There is nothing for the developer to choose beyond the form itself, and no repository is selected in the form. How the request is written, directly through the Kubernetes API as the signed-in user or
committed to a Git repository, depends on the backend's ske.mode setting rather than on this
mode.
To choose the namespace for developers instead of asking them to type it, see Request namespace.
Nothing is under review here, so the record exists only to make the request visible while the platform catches up. See What a developer sees while waiting.
Pull-request mode
@syntasso/plugin-ske-backend must be at least v0.21.0, and @syntasso/plugin-ske-frontend at
least v0.18.0. Earlier backend versions do not provide the action that
pull-request mode uses to record a request, so a submission opens the pull
request and then fails.
The generated Template gains a Repository & Credentials page asking which repository the
request should be raised against, and then runs three steps. For a Promise whose kind is Postgresql
they are named:
- Preparing your Postgresql request, writing the request manifest.
- Opening a pull request for review, against the chosen repository.
- Registering your Postgresql request in the catalog, telling the Backstage backend that a request is now awaiting approval.
The request manifest lands in the repository at:
kratix-requests/<promise>/<namespace>/<name>.yaml
Nothing reaches the platform until that pull request is merged. Merging it commits the manifest, and your existing GitOps tooling applies it.
Branches and repositories
The developer picks the repository per run, so different teams can raise requests against their own
repositories through the same Template. Pull requests target main by default.
Branches are named backstage/<promise>/<name>, and a re-run for the same resource reuses the
existing branch and pull request rather than opening a second one. Deletions use a separate
backstage/<promise>/<name>-delete branch, so requesting a deletion can never silently amend an
in-flight update.
Pull-request mode uses GitHub's pull request API and the repository picker only accepts
github.com. GitLab, Bitbucket and Azure repositories are not supported in this mode. Use push
mode with the backend's ske.mode: gitops if you need a different provider.
Updating and deleting
Both go through review as well, from the Manage tab on the resource in Backstage:
- Update re-opens the Template pre-filled with the resource's current values, with the name and namespace locked, and raises a pull request with the changes.
- Delete raises a pull request that removes the request manifest. The resource stays until that pull request is merged, and the confirmation dialog says so.
Deletion only routes through review for resources that were themselves requested through
pull-request mode. A resource created some other way, by kubectl or under push mode, is deleted
immediately.
What a developer sees on the task page
The step names above are the same for a create and an update, because one Template serves both. The
frontend plugin's scaffolderModuleResourceRequestCopy
module, which the install steps add to your app, renames the steps on the task page to say whether
the run creates, updates or deletes the resource, for example "Updating dev-postgres Postgresql
request". Tasks from other Templates are shown as Backstage shows them.
What a developer sees while waiting
A resource takes time to reach the catalog: the platform has to create and reconcile it, the portal has to generate its entity, and Backstage has to discover it. Both modes fill that gap the same way, with a pending request entity published as soon as the request is recorded and withdrawn once the real resource entity appears.
| Mode | Banner | Links |
|---|---|---|
push | Submitted. Waiting for the platform to create this resource. | None; nothing was opened |
pull-request | Waiting for approval. This resource will be provisioned when the pull request is merged. | The pull request awaiting approval |
The recorded request appears and is withdrawn through events; ske.pendingRequests.refreshSeconds
is a backstop. How long the real entity takes to arrive depends on those three steps. Under push with ske.mode: kubernetes
that can be seconds. A mode that commits to Git adds your GitOps agent's sync interval, and
Git-based catalog discovery adds its own polling interval.
Where a completed task leads
When the run finishes, the task page includes links to the catalog item and open pull requests:
| Mode | Links on the completed task |
|---|---|
push | View in Catalog |
pull-request | View in Catalog, then Pull Request |
View in Catalog goes to the resource's own entity when the platform has already published one, and to the pending request otherwise. A create therefore lands on the pending request, since the resource does not exist yet; an update lands on the resource.
Customizations and delivery modes
Portal Patch recipes that target the ske-configure-resource step,
including the hideNamespaceManifest recipe, do not work under pull-request mode. That step only
exists in push mode, so the recipe matches nothing, fails, and aborts the sync, which means no
entity ships at all.
If a portal uses pull-request mode, avoid recipes that target that step.
Both modes emit a record-request step carrying a copy of the request manifest. Under push mode, a
hideNamespaceManifest recipe that targets only ske-configure-resource leaves that step referring
to the objnamespace parameter the same recipe set deletes.
Nothing fails at sync time. The recorded namespace resolves to nothing, so the request no longer matches the resource entity the platform ships, and the pending request is never withdrawn from the catalog.
Patch the record step as well:
target:
kind: Template
list:
path: spec.steps
key: id
value: record-request
op: hideNamespaceManifest
field: input.manifest
defaultNamespace: my-namespace
---
target:
kind: Template
list:
path: spec.steps
key: id
value: record-request
patch:
input:
namespace: my-namespace
Only recipes reproducing hideNamespace need this. A Promise that sets the generator's own
templateHideNamespace field gets a correct record step without any recipe at all.
Recipes that only patch the generated documents, such as setting an owner, adding tags, or
singleSpecPage, work in both modes.
Choosing
Start with push unless you have a reason not to. It is simpler, has no repository prerequisites,
works with any Git provider through ske.mode: gitops, and has no interaction with Portal Patch to
think about.
Move to pull-request when requests genuinely need human approval, and you are on GitHub.
