Delivery modes
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 Configure Backstage.
Push mode
The generated Template asks for the resource name, namespace, and the Promise's own spec fields,
then runs a single step that applies the request.
There is nothing for the developer to choose beyond the form itself, and no repository is
involved. 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.
Pull-request mode
The generated Template gains a Repository & Credentials page asking which repository the request should be raised against, and then runs three steps:
- Generate resource request, writing the request manifest.
- Pull Request, opening a pull request against the chosen repository.
- Record request, 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 while waiting
Once a request is recorded, Backstage shows a pending request entity in the catalogue, carrying a link to the pull request that is awaiting approval. It disappears once the request is merged and the real resource entity appears in the catalogue.
There is a delay between the two, since Backstage has to re-read both the pending requests and your catalogue. Expect around a minute by default.
There is no link to the pull request on the scaffolder's completion page. Developers find their pull request through the pending request entity in the catalogue.
Customizations and pull-request mode
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.
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.
