Creating Service Providers
Codesphere allows you to publish existing Codesphere landscapes as Managed Service Providers. This enables other developers to discover and deploy your custom solutions.
Landscape-based Service Providers
A landscape-based service provider enables others to instantiate your Codesphere landscape as a pre-configured managed service that they can deploy and manage through the service catalog.
info
Publishing providers requires cluster admin permissions. Team admins can request providers to be scoped to specific teams.
Prerequisites
Before creating a landscape-based service provider, you need:
- A Git repository containing a valid Codesphere landscape with a
ci.ymlfile. - A provider definition, either as a
provider.ymlfile at the repository root or provided directly in the API request. - Git access to the landscape repository. Codesphere pulls the repository through your Git connection, so the account you use to create the provider must have permission to access it. You can manage your Git connections in your user settings.
For an example, take a look at our public URL-Shortener Repository.
Git access is tied to a user
The Git connection of the user who created (or last updated) a provider is used for pulling the landscape repository. If that user loses access to the repository, deployments of the provider will fail. To transfer the provider to another user's Git connection, that user sends a PUT or PATCH request for the provider — their connection is then used for pulling. For long-lived providers, consider publishing with a technical user.
Provider Definition
The provider.yml file defines all metadata and configuration schemas for your service provider.
name: mattermost
schemaVersion: v1
author: Your Team
displayName: Mattermost
iconUrl: https://example.com/mattermost-icon.png
category: collaboration
description: |
Open-source team messaging and collaboration platform.
Supports channels, direct messaging, and file sharing.
teamSingleton: true
backend:
landscape:
gitUrl: https://github.com/your-org/mattermost-landscape
configSchema:
type: object
properties:
SITE_NAME:
type: string
description: Display name for your Mattermost instance
default: mattermost
readOnly: false
MAX_USERS:
type: integer
description: Maximum number of users allowed
x-update-constraint: increase-only
required: ['MAX_USERS']
secretsSchema:
type: object
properties:
ADMIN_PASSWORD:
type: string
format: password
detailsSchema:
type: object
properties:
hostname:
type: string
port:
type: integer
plans:
# The plan has no influence on the resource usage, so you should only define one.
# You can also set plans to an empty array to disable the plan selection UI.
- id: 0
name: Default Plan
description: Resource usage depends on the ci-profile of the landscape.
parameters: {}
versions:
0.0.1:
ciProfile: debug
# gitRef can be a commit-hash, branch or tag
gitRef: 1a410efbd13591db07496601ebc7a059dd55cfe9
description: Initial release
1.0.0-rc:
appVersion: Nextcloud v33
ciProfile: prod
gitRef: release-branches/1-0-0-rc
description: |
Release Candidate.
Changelog:
- Feature X
- Feature Y
- Fix Z
1.0.0:
appVersion: Nextcloud v33
ciProfile: prod
gitRef: release-tags/1-0-0
description: Long Term Support Release
Provider Fields
| Field | Description |
|---|---|
name | Unique identifier for the provider. Must match ^[-a-z0-9_]+$. |
schemaVersion | Version string in the format v[0-9]+ (e.g., v0, v1). Together with name it uniquely identifies a provider. |
displayName | Human-readable name shown in the Marketplace UI. |
iconUrl | URL to the provider icon (absolute or relative path). |
category | Grouping category (e.g., databases, messaging, monitoring). |
author | Organization or individual responsible for the provider. |
description | Markdown-formatted description of the service. |
teamSingleton | Optional boolean. When true, each team can have only one non-deleted service for this provider and version. A service that reaches deleted status no longer blocks recreation; a service still being deleted does. Omit or set to false to allow multiple services. |
backend.landscape.gitUrl | Git repository URL containing the landscape configuration. |
configSchema | OpenAPI schema defining user-configurable options. These values are passed to the landscape as environment variables. |
secretsSchema | OpenAPI schema defining secret values (e.g., passwords). These values are set in the landscape's secrets vault. |
detailsSchema | OpenAPI schema defining runtime details exposed after provisioning. See Default landscape details. |
versions | Versions of the service that users can deploy. At least one version is required. See Provider Versions. |
The three schemas share one format — a valid OpenAPI schema object
configSchema, secretsSchema, and detailsSchema serve different purposes but must each be a valid OpenAPI schema object — that is how Codesphere parses them. As in OpenAPI, properties can be marked as required or given sensible default values.
Below you can see where each schema type appears in the Codesphere UI:
- ConfigSchema
- SecretsSchema
- DetailsSchema
The configSchema defines the configuration properties the user sets when creating a service — for example, the PostgreSQL version to deploy. These values are passed to the landscape as environment variables (see Passing Configuration to Landscapes). Once the service is created, the configuration can be inspected in the service settings. Properties that are not marked as readOnly can be updated later (see Update Constraints).

caution
Updating configuration parameters may cause a short downtime of the service.
Secrets defined in the secretsSchema appear as form inputs in the create service dialog and must be filled in by the user — for example, the PostgreSQL superuser password. The values are injected into the service during creation.

Properties defined in the detailsSchema are provided by the service at runtime — for example, the PostgreSQL hostname. They are read-only and can be inspected in the details section of the service settings.

Property keys are parsed into human-readable labels in the UI
Codesphere does not show the raw keys to users — it pretty-parses them into human-readable labels in the UI (for example, service_url_frontend_3000 is displayed as Service Url Frontend 3000).
Default landscape details
For landscape-based providers, the following details are always computed and returned automatically:
| Key | Value |
|---|---|
hostname | The landscape's URL. |
service_url_<serverName>_<port> | The URL of a service, one entry per service defined in the landscape. |
For example, a landscape with a frontend service on port 3000 produces service_url_frontend_3000. To show these values on the service's details page, declare them in your detailsSchema:
detailsSchema:
type: object
properties:
hostname:
type: string
format: uri
service_url_frontend_3000:
type: string
format: uri
Provider Versions
Every provider must define at least one entry in versions. Each version pins the exact state of the landscape that gets deployed:
gitRef: A commit hash, branch, or tag of the landscape repository.ciProfile: The CI profile from the landscape'sci.ymlto use for deployment.appVersion(optional): A human-readable label for the deployed application version.description(optional): Markdown-formatted release notes shown to the user.
When creating a service, users choose which version to deploy. Users can change the version of their deployed services, triggering an upgrade or downgrade. For more information, see Upgrading to a new Version.
Deprecated fields
backend.landscape.ciProfile and backend.landscape.gitRef used to define these values once for the whole provider. They are deprecated — ciProfile and gitRef are now defined per version — but remain available for backward compatibility.
Publishing and Updating a Landscape-based Provider
The recommended way to publish and update a provider is the idempotent upsert endpoint of the Codesphere Public API. Send a PUT request to /managed-services/providers:
- If no provider with the same
nameandschemaVersionexists yet, it is created. - If one already exists, all mutable fields are updated in place.
This means you can use the same request to create a provider and to roll out later changes — you don't need to track whether the provider already exists. This is useful in CI/CD pipelines, for example.
You can either provide a Git URL that includes the provider.yml file or the full provider specification in the payload.
tip
Dedicated create (POST) and update (PATCH) endpoints still exist for partial updates and more explicit workflows, but for most cases the upsert endpoint is the simplest choice.
Versions are append-only
You can add new entries to versions, but existing version records cannot be modified or removed through an upsert. All other provider fields are updated in place.
- Fetch provider.yml from Git
- Provide full specification
The simplest approach is to provide the Git repository URL. Codesphere will fetch and validate the provider.yml file from your repository automatically.
note
If you omit gitRef, the default branch of the repository is used to fetch the provider.yml file.
The scope is not part of the provider.yml — you provide it here, in the publish request, alongside the Git URL:
curl -X PUT "https://<your-codesphere-url>/api/managed-services/providers" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"gitUrl": "https://github.com/your-org/mattermost-landscape",
"gitRef": "my-branch",
"scope": {
"type": "global"
}
}'
For more control, or if you don't want to include a provider.yml in your repository, you can provide the complete provider specification directly in the API request:
curl -X PUT "https://<your-codesphere-instance-url>/api/managed-services/providers" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "mattermost",
"schemaVersion": "v1",
"author": "Your Team",
"displayName": "Mattermost",
"iconUrl": "https://example.com/mattermost-icon.png",
"category": "collaboration",
"description": "Open-source team messaging platform.",
"teamSingleton": true,
"backend": {
"landscape": {
"gitUrl": "https://github.com/your-org/mattermost-landscape",
"ciProfile": "production"
}
},
"configSchema": {
"type": "object",
"properties": {
"SITE_NAME": {
"type": "string",
"description": "Display name for your Mattermost instance"
},
"MAX_USERS": {
"type": "integer",
"description": "Maximum number of users allowed"
}
}
},
"secretsSchema": {
"type": "object",
"properties": {
"ADMIN_PASSWORD": {
"type": "string",
"format": "password"
}
}
},
"detailsSchema": {
"type": "object",
"properties": {
"hostname": { "type": "string" },
"port": { "type": "integer" }
}
},
"plans": [],
"scope": {
"type": "team",
"teamIds": [42, 43]
},
"versions": {
"0.0.1": {
"ciProfile": "debug",
"gitRef": "1a410efbd13591db07496601ebc7a059dd55cfe9",
"description": "Initial release"
},
"1.0.0-rc": {
"appVersion": "Mattermost v10",
"ciProfile": "prod",
"gitRef": "release-branches/1-0-0-rc",
"description": "Release Candidate.\nChangelog:\n - Feature X\n - Feature Y\n - Fix Z\n"
},
"1.0.0": {
"appVersion": "Mattermost v10",
"ciProfile": "prod",
"gitRef": "release-tags/1-0-0",
"description": "Long Term Support Release"
}
}
}'
Provider Scopes
When creating a provider, you can define its visibility scope:
| Scope Type | Description |
|---|---|
global | Available to all teams in the Codesphere instance. Requires cluster admin permissions. |
team | Available only to specified teams. Provide an array of teamIds. |
Make the provider available to everyone in the instance:
"scope": {
"type": "global"
}
Restrict the provider to specific teams:
"scope": {
"type": "team",
"teamIds": [1, 24, 56]
}
Team-singleton providers
Set teamSingleton: true when a provider should expose at most one non-deleted managed service per team for a specific provider version. Codesphere rejects a second create request with an AlreadyExists error while an existing service for that team, provider, and version has not reached deleted status. This includes services that are still being deleted. Once the existing service reaches deleted, the team can create another one.
Leave teamSingleton unset or set it to false when teams may create multiple services from the same provider version.
Passing Configuration to Landscapes
When a service is created from a landscape-based provider, the user's input is passed down to the landscape: properties from the configSchema are set as environment variables, and secrets from the secretsSchema are injected into the landscape's vault. Both can be referenced in the landscape's ci.yml:
schemaVersion: v0.2
run:
my-service:
steps:
- command: ./start.sh
env:
APP_VERSION: ${{ workspace.env['APP_VERSION'] }}
ADMIN_PASSWORD: ${{ vault.ADMIN_PASSWORD }}
In this example, APP_VERSION is a property of the provider's configSchema and ADMIN_PASSWORD is a property of the provider's secretsSchema.
Update Constraints with x-update-constraint
The configSchema supports a custom extension x-update-constraint that lets you restrict how individual properties can be changed
after a service has been created. This is useful for enforcing operational rules — for example, preventing storage from being
decreased or locking down a database engine version after initial setup.
Add the x-update-constraint keyword to any property in your configSchema:
configSchema:
type: object
properties:
storage:
type: integer
description: Storage allocation in GB
x-update-constraint: increase-only
version:
type: string,
description: Version of the Postgres DB.
enum: ['17.6', '16.10', '15.14', '14.19', '13.22']
x-update-constraint: immutable,
Available Constraints
| Constraint | Behavior |
|---|---|
increase-only | The new value must be greater than or equal to the current value. Only applies to numeric fields. |
immutable | The property cannot be changed once it has been set. |
info
Update constraints are only enforced when updating an existing service. During initial creation, all values are accepted as long as they pass the standard schema validation.
Supported Formats
Provider schemas support the standard OpenAPI format values for validation:
int32, int64, float, double, byte, binary, date, date-time, password, uri, hostname
Beyond validation, some formats change how a value is rendered in the Codesphere UI:
| Format | Effect in the UI |
|---|---|
uri, hostname | In the details section of the service settings, the value is rendered as a clickable link instead of plain text. Use it for detailsSchema properties such as hostname or service_url_*. |
password | In the create service dialog, the corresponding secretsSchema field is rendered as a masked password input rather than a plain text field. |
For example, marking the detail URLs as format: uri turns them into links on the details page, and marking a secret as format: password masks it on entry:
detailsSchema:
type: object
properties:
hostname:
type: string
format: uri # rendered as a link in the details section
readOnly: true
secretsSchema:
type: object
properties:
admin_password:
type: string
format: password # rendered as a masked input in the create dialog

Dynamic Details with x-endpoint
The detailsSchema supports a custom OpenAPI extension x-endpoint that allows you to fetch runtime details dynamically from your service. This is useful for retrieving live status information, metrics, or other data that changes after provisioning.
When x-endpoint is set on a property, Codesphere fetches the value from the specified endpoint at runtime and validates it against the property's schema.
Templating the endpoint URL
The endpoint URL is a template: you can reference other detail fields with the ${{ .field_name }} syntax, and Codesphere substitutes their values before making the request. This lets you build the endpoint from details that are only known after provisioning — such as the default service_url_* fields.
detailsSchema:
type: object
properties:
service_url_frontend_3000:
type: string
format: uri
readOnly: true
status:
type: object
properties:
state:
type: string
# ${{ .service_url_frontend_3000 }} is replaced with that detail's value at runtime
x-endpoint: '${{ .service_url_frontend_3000 }}/health'
In this example, the status property is fetched from the frontend service's /health endpoint. The template resolves ${{ .service_url_frontend_3000 }} to the running service's URL, so the final request goes to <service-url>/health, and the JSON response populates status.state.
info
Only GET requests are supported for x-endpoint. The endpoint must return JSON matching the property's schema definition.
Custom REST Backends
Codesphere's Managed Services architecture is designed to be extensible. If landscape-based providers don't meet your specific requirements you can implement your own custom REST backend.
tip
For a complete guide on implementing the Managed Service Adapter API specification, see Create a Custom REST Backend.