Skip to content

Apps / Service Catalog

The DPF (Deployment Platform Framework) lets you deploy managed applications from the b'nerd service catalog. Apps are containerized workloads provisioned and operated on your Kubernetes clusters — databases, monitoring stacks, message brokers, and similar services.

The API is stable. Available app kinds depend on your organization's catalog subscription — contact your account manager to confirm which kinds are active on your organization.

Managed apps (Stack-placed)

b'nerd's managed-apps framework lets you run enterprise-grade, operator-backed software on your Kubernetes clusters without operating it yourself — installation, upgrades, and ongoing reconciliation are handled by a dedicated operator per app kind. Managed Nextcloud, Managed GitLab, and Managed Keycloak are the kinds built on this framework today; more are on the way. Everything in this section — Stack placement, the five-phase lifecycle, write-only credentials, and the day-2 operations below — is the shared mechanism every managed kind uses, not something specific to any one of them.

Managed apps differ from the generic CRUD flow above in three ways: they're placed onto a Stack, they carry an explicit five-phase lifecycle instead of a bare status object, and (for kinds that need one) they collect an administrator credential once, write-only, at order time. The rest of this section explains the concepts; for a step-by-step walkthrough see Deploying a Managed App.

What a Stack is

A Stack is a Kubernetes cluster that b'nerd has registered and confirmed can run a given category of managed workloads. When you create a managed app you choose (or the create flow auto-selects, when there's only one choice) the Stack it should run on. You don't manage the Stack inventory yourself — b'nerd's platform team registers and maintains Stacks; your view is limited to the ones eligible for the app you're creating.

Every Stack has a visibility that determines which organizations may place apps on it:

Visibility Meaning
public Open to any organization on the platform.
shared Enabled for a specific set of organizations.
private Dedicated to a single organization.

Eligibility — which Stacks show up as choices — is decided by the platform: only Stacks that are confirmed to support the app kind you're deploying, and that your organization is authorized to use, are ever offered. If nothing is eligible yet for a kind, the create flow says so directly (with a link to contact support) instead of showing an empty or misleading list — the kind itself can still be "available" in the catalog even while no Stack currently hosts it; b'nerd is notified and will wire one up.

The instance lifecycle

Every managed-app instance moves through up to five phases:

Phase Meaning
requested Accepted by the platform; not yet started.
provisioning Actively being installed or reconfigured.
ready Up and running. The instance's URL becomes available.
failed Something went wrong. The instance page shows the reason.
deleting Being torn down after you requested deletion.

Status is refreshed on a short polling interval, not pushed live. The instance page shows when it was last synced; if that's more than a few minutes ago it tells you the status may be slightly out of date rather than implying it's live.

Configuring an instance (spec)

Kind-specific settings — the knobs each managed kind exposes beyond name and Stack — are submitted as a nested spec object in the create request body (and in a PATCH for day-2 changes other than delete):

POST /apps
Authorization: Bearer <token>
Content-Type: application/json

{
  "kind": "Apps::Managed::Gitlab::V2",
  "name": "my-gitlab",
  "project_id": "<uuid>",
  "spec": {
    "version": "17.1.0",
    "edition": "ce"
  }
}

Each kind only accepts its own allowlisted spec keys; an unrecognized key is rejected with a 422 validation_failed response. Currently:

Kind spec fields
Managed Nextcloud version, phpMemoryLimit, maintenanceWindow
Managed GitLab version, edition (ce or ee)
Managed Keycloak version, root_redirect — see Managed Keycloak

Deprecated: flat top-level version

A bare top-level version field (not nested under spec) is still accepted on Nextcloud requests for one release, for backward compatibility with integrations built before the spec envelope shipped. It is deprecated and will be removed once the CLI, Terraform provider, and dashboard have all migrated to spec — check the release notes for the exact removal release before relying on it. New integrations, and every GitLab request, should use the nested spec form.

Managed GitLab

Managed GitLab runs both Community Edition (CE) and Enterprise Edition (EE), selected via spec.edition. If omitted, edition defaults to ce.

  • CE — no credentials are collected at order time; nothing beyond spec is required.
  • EE — requires an EE license, submitted once as a write-only credential at create time (credentials.license.license, the license file contents). Same write-only handling as any other credential: it's stored encrypted and never returned by the API or shown again in the dashboard.

The instance's database, cache, and object storage are always fully managed by the platform — there's nothing to configure or bring your own for these; they come up automatically as part of provisioning.

Known limitations at this release

  • Outbound email (SMTP) is not yet configurable for Managed GitLab instances.
  • Enterprise Edition combined with the managed Elasticsearch integration has not yet been broadly validated in production — talk to your account manager before relying on this combination.

Managed Keycloak

Managed Keycloak's customer-facing surface — create flow, the default instance host, and the admin credential's reveal/audit model — is documented on its own page: Managed Keycloak.

Credentials are write-only

Managed-app kinds that need an administrator login — Nextcloud today, with more kinds following the same pattern — collect that credential once, in the create flow's Configure step, and treat it as write-only from that point on:

  • The value you submit is encrypted and materialized directly into a Kubernetes Secret inside the instance's own namespace — it isn't held anywhere in the create flow after submission.
  • There is no "reveal password" affordance for these kinds — no read endpoint or dashboard view ever returns the credential once it's been set. Managed Keycloak is the one exception: its admin credential can be revealed again later by a project admin, with every reveal audit-logged — see Managed Keycloak — Admin credentials for that model specifically.
  • If you'd rather not choose your own value, the Configure step can generate a random one for you locally, in your browser. It's shown once, with a copy-to-clipboard control, and is gone the moment you leave the page — save it before you move on.

Day-2 operations

Two operations are available from an instance's detail page once it's ready:

  • Change version — swap to a newer version of the underlying operator's release. Only upgrades are offered; downgrades aren't supported. Changing version re-enters the provisioning phase while the operator reconciles.
  • Delete — a two-step, deliberately friction-heavy action. Instances are created with deletion protection on by default; you turn it off (typed confirmation required) before the delete action itself becomes available (a second typed confirmation). This is covered in full in the Deploying a Managed App guide.

Endpoints

Method Path Description
GET /apps List apps
POST /apps Create an app
GET /apps/{id} Show an app
PATCH /apps/{id} Update an app
DELETE /apps/{id} Delete an app

List apps

GET /apps
Authorization: Bearer <token>

Returns all apps visible to the authenticated account across all projects in the organization.

Create an app

POST /apps
Authorization: Bearer <token>
Content-Type: application/json

{
  "app": {
    "name": "my-postgres",
    "kind": "postgres",
    "organization_id": "<uuid>",
    "project_id": "<uuid>"
  }
}

Returns 201 Created. The kind value determines which service from the catalog is deployed. Available kinds depend on your organization's catalog subscription — query GET /apps to see deployed kinds or contact your account manager for the current catalog.

Show an app

GET /apps/{id}
Authorization: Bearer <token>

Key response fields:

Field Description
id App UUID
name Display name
kind App type (e.g. postgres, redis)
project_id Project the app belongs to
organization_id Organization UUID
status Current deployment status object (nullable while provisioning)
stack_id Underlying stack identifier (nullable)
parent_app_id Parent app UUID for child/plugin apps (nullable)
deleted_at Soft-delete timestamp; null for active apps

Update an app

PATCH /apps/{id}
Authorization: Bearer <token>
Content-Type: application/json

{ "app": { "name": "renamed-postgres" } }

Delete an app

DELETE /apps/{id}
Authorization: Bearer <token>

Returns 204 No Content. Deleting an app deprovisions the underlying workload.

Irreversible for stateful services

Deleting a stateful app (e.g. a database) permanently removes its data. Ensure you have taken a backup or snapshot before proceeding.

Webhooks and reconciliation

Apps integrate with the platform's webhook system. When a container image is pushed to the b'nerd registry and a webhook target mapping matches the resource_url, the corresponding app is reconciled automatically — pulling the new image tag and rolling out the update.

Admin API (superadmin)

Admin-only

The /admin/apps/* endpoints require a platform-admin account. They are not reachable with an organization-scoped token, and are not part of the interactive API Reference — this section documents the ones customers most often ask support about.

Change an app's plan

PATCH /admin/apps/{id}/plan
Authorization: Bearer <admin-token>
Content-Type: application/json

{ "plan": "enterprise" }

Changes an operator-backed app's plan and re-enqueues reconciliation, so the new plan's sizing (replicas, PostgreSQL topology, PgBouncer) flows into the running instance the same way POST /admin/apps/{id}/reconcile does. The instance stays available during the change — see Managed Keycloak — Plans for the customer-facing plan table and how to request a change.

Reverting a plan change is the same call with the previous plan key.

Response 200 — admin app object, data.plan set to the new key.

Response 403 — not a platform-admin account.

Response 404 — unknown app id.

Response 422

{
  "request_id": "0197abc-...",
  "errors": [
    {
      "code": "invalid_plan",
      "message": "Unknown plan 'no-such-plan' for this app kind",
      "status": 422,
      "field": "plan",
      "source": { "service": "apps", "operation": "set_plan" },
      "meta": { "retryable": false }
    }
  ]
}

invalid_plan — the plan key doesn't exist, isn't active, or doesn't belong to this app's kind. plan_change_unsupported — the app's kind doesn't support plan changes through this endpoint.


Reconcile an app

POST /admin/apps/{id}/reconcile
Authorization: Bearer <admin-token>

Re-runs the app's reconcile pass against its operator binding on demand — the same code path a scheduled reconcile or a plan change uses. Useful when an instance needs to pick up a CR or catalog change without waiting for the next scheduled pass.

Response 200 — admin app object.

Response 404 — unknown app id.


Authorization

App operations use the apps permission category:

Operation Required permission
List / show apps:read
Create apps:create
Update apps:update
Delete apps:delete

See Roles & RBAC for which roles carry these permissions.

Troubleshooting

Symptom Likely cause Fix
status is null after create App is still provisioning Poll GET /apps/{id} until status is non-null
403 on create Missing apps:create permission Check your membership permissions
App stuck in a failed state Upstream cluster issue Contact support with the app id and stack_id