Marketplace workload deployment
This article refers to SDK version v0.5.0. The current SDK version is N/A.
Overview
A Marketplace app is one published in Barbara's marketplace, as opposed to a Docker app you author yourself (see Deploying your own Docker apps). Deploying one through the SDK covers the same three steps as the Deploying Marketplace apps wizard: pick the app and version, describe its services, and configure it.
Compose Config. The step in Barbara Panel's deployment wizard where an operator maps a Marketplace app's ports and volumes to values for one specific node, without ever touching the underlying docker-compose.yaml.
Before an app shows up at all, it needs to already be in your organization's App Library. See Add a Marketplace app to your Library if it isn't yet.
Find the application version
apps = client.applications.list(search="mqtt")
versions = client.applications.list_versions("<application-id>")
client.applications.list(*, search=None, size=None) searches the same catalog as the Library view's search bar: pass a substring of the app's name in search to narrow down the results, and read the id off the matching Application object. list_versions(application_id) returns List[Dict[str, Any]] (the response wraps the array as {"appVersionList": [...], "total": ...} internally, and the SDK unwraps that for you), one entry per version listed in the app's detail page in Panel; each entry's _id key is the app_version_id the deployment call below expects.
Deploy it
client.nodes.workloads.create_marketplace_workload(
node.id,
app_version_id="<app-version-id>",
application_id="<application-id>",
name="my-mqtt-broker",
compose_config=[{"name": "broker", "ports": {"PORT_NUMBER": "1883"}}],
)
create_marketplace_workload(node_id, *, app_version_id, application_id, name, run_docker=True, force_pull=False, enable_logs=True, gpu=None, config=None, config_id=None, compose_config=None, app_secrets=None) on client.nodes.workloads:
| Parameter | Type | What it is |
|---|---|---|
node_id | str | The target node's internal id |
app_version_id | str | The version's _id from list_versions, as above |
application_id | str | The application's id, as above |
name | str | A name for this specific deployment, distinct from the application's own name; useful when you deploy the same app more than once on different nodes |
run_docker | bool | Start the container immediately, mirroring Run automatically after install in the wizard's Advanced section |
force_pull | bool | Re-download Docker images even if cached, mirroring Force Docker image download |
enable_logs | bool | Collect logs for this workload from the start, mirroring Activate logs for this application |
gpu | Optional[bool] | Whether to expose the node's GPU to the container, when the node has one |
config | Optional[Dict[str, Any]] | An inline App Config JSON document, the same payload as wizard step 3 (application configuration) |
config_id | Optional[str] | The id of a reusable client.configs entry instead of an inline config |
compose_config | Optional[List[Dict[str, Any]]] | Ports and volumes for this deployment |
app_secrets | Optional[List[Dict[str, Any]]] | Env vars for this deployment |
compose_config and app_secrets are the fields that need the most care, since their shape comes from the Marketplace app's own template rather than from the SDK. Both are lists of per-service dicts with a name key matching a service declared in the app's docker-compose.yaml (find it via the Docker Compose Info segment of a deployed workload card, or the app's Marketplace Technical Notes): compose_config entries take a ports key (mapping the port variable name the app's README documents, for example PORT_NUMBER, to the host port you want exposed, as a string) and a volumes key; app_secrets entries take an env key. The SDK splits these into two parameters, matching Panel's own separate Compose Config and App Secrets steps. Check the app's README for the exact keys each service accepts, the SDK doesn't validate this shape ahead of the API, it just forwards whatever dicts you build.
Creation calls don't return the resulting workload state, the API acknowledges the request but doesn't echo back the deployed object. Call get(...) afterwards if you need to inspect the result.
Check on it
workload = client.nodes.workloads.get(node.id, "<workload-id>")
logs = client.nodes.workloads.get_logs(node.id, "<workload-id>")
get(node_id, workload_id) returns a Workload. Unlike most SDK entities, Workload.id isn't returned by the API as a distinct top-level field, it's the same workload_id you already passed in, kept on the object purely for symmetry with other dataclasses, and the same dataclass is shared with cluster-scope workloads, since the API mirrors the same shape at both scopes; everything else about the deployed workload (its status, services, container info) lives in workload.raw, since the shape varies too much by workload kind to type fully. See Workload lifecycle for starting, stopping, and reading logs from an already-deployed workload in depth.
Update it
client.nodes.workloads.update_marketplace_workload(
node.id,
"<workload-id>",
app_version_id="<new-app-version-id>",
application_id="<application-id>",
compose_config=[{"name": "broker", "ports": {"PORT_NUMBER": "1883"}}],
)
update_marketplace_workload takes the same keyword parameters as create_marketplace_workload, plus the workload_id as a required positional argument, plus an optional name keyword to rename the deployment. Point app_version_id at a newer version to roll the app forward, the same effect as clicking Update version on the workload card's Status segment.
If only one of compose_config/app_secrets is given, the other side is read back from the workload's current state first and resent unchanged, so passing just one here doesn't clear the other.
If you only need to change one of the two without touching the version, narrower calls skip the rest of the update payload:
client.nodes.workloads.update_marketplace_workload_compose_config(
node.id, "<workload-id>", [{"name": "broker", "ports": {"PORT_NUMBER": "1884"}}],
)
client.nodes.workloads.update_marketplace_workload_app_secrets(
node.id, "<workload-id>", [{"name": "broker", "env": {"LOG_LEVEL": "debug"}}],
)
update_marketplace_workload_compose_config(node_id, workload_id, compose_config) changes only ports/volumes; update_marketplace_workload_app_secrets(node_id, workload_id, app_secrets) changes only env vars. Each reads the other side of the workload's current state first and resends it unchanged, since the underlying API replaces the entire services array in one PUT rather than supporting a partial per-field update.
To read either side back without a full get():
compose_config = client.nodes.workloads.get_compose_config(node.id, "<workload-id>")
app_secrets = client.nodes.workloads.get_app_secrets(node.id, "<workload-id>")
get_compose_config and get_app_secrets return one entry per service that has any matching field set, in the same shape their respective update methods take.
Changing compose_config or app_secrets is a build-time change: it triggers a rebuild of the Docker Compose stack, matching what Application configuration types documents for Compose Config. Expect a brief interruption of the workload while it rebuilds.
Deploy at cluster scope
The same shape deploys across an entire cluster in one call instead of node by node, see Clusters & cluster workloads:
client.clusters.workloads.create_marketplace_workload(
"<cluster-id>",
app_version_id="<app-version-id>",
application_id="<application-id>",
name="my-mqtt-broker",
compose_config=[{"name": "broker", "ports": {"PORT_NUMBER": "1883"}}],
)
client.clusters.workloads.create_marketplace_workload takes the same app_version_id, application_id, name, compose_config, and app_secrets as the node-level call above, with a cluster_id in place of node_id, and drops run_docker (a cluster workload deploys across every eligible node in the cluster, there's no single "run immediately" toggle per node the way there is for one workload). Barbara's product docs use "Workload" at both scopes, there's no separate "stack" concept in Panel, and client.clusters.workloads matches that. See Clusters & cluster workloads for the cluster-only deployment and placement_constraints parameters.
Summary
You picked an application version from the Marketplace catalog, deployed it to a node with its Compose Config, and checked on the result.
The same compose_config shape and lifecycle methods apply whether you're deploying to one node or, via client.clusters.workloads, to a whole cluster.
Continue to Deploying your own Docker apps for applications you build yourself, or see client.nodes.workloads and client.applications in the Reference for the full method list.