Workload lifecycle
This article refers to SDK version v0.5.0. The current SDK version is N/A.
Overview
Once a workload is deployed, whether a Docker app, a Marketplace app, or a Model, the day-to-day operations are the same regardless of which kind it is. Everything here maps to the Marketplace apps workload card and its segments in Panel: Status, Logs, App Config, Info.
Every method below takes a node_id and a workload_id. The workload_id is the internal identifier of the deployed workload, Workload.id from a create_*_workload call's follow-up get(), or read off client.nodes.get(node_id).raw["spaces"] if you need to enumerate everything running on a node.
Workload. What Barbara Panel calls one deployed application instance, shown as its own card with Status, Logs, App Config, and Info segments, whether it's a Docker app, a Marketplace app, or a Model.
Learn moreStart and stop
client.nodes.workloads.start(node.id, "<workload-id>")
client.nodes.workloads.stop(node.id, "<workload-id>")
start(node_id, workload_id) and stop(node_id, workload_id) take no other parameters and return None; they map directly to the Start / Stop buttons in the workload card's Status segment.
Neither call blocks until the container has actually finished starting or stopping, so a get() right after may still show the previous state. See Check status below for refresh_info if you need the freshest read.
Read logs
logs = client.nodes.workloads.get_logs(
node.id, "<workload-id>", results=100, search="error"
)
client.nodes.workloads.enable_logs(node.id, "<workload-id>", True)
get_logs(node_id, workload_id, *, from_=None, to=None, results=100, search=None) returns List[Dict[str, Any]], one dict per log line, the same content the workload card's Logs segment streams:
| Parameter | Type | What it does |
|---|---|---|
from_ | Optional[str] | ISO 8601 timestamp; only return lines logged at or after this time |
to | Optional[str] | ISO 8601 timestamp; only return lines logged at or before this time |
results | int | Maximum number of lines to return, default 100 |
search | Optional[str] | Substring filter, the same free-text filter as the Filter field in the Logs segment |
enable_logs(node_id, workload_id, enabled) toggles whether the node collects logs for this workload at all, useful to turn off for a noisy container you don't need to audit, mirroring the Enable switch in the Logs segment; logs_enabled(node_id, workload_id) reads the current value back as a plain bool.
If enable_logs is currently False for a workload, get_logs returns whatever was already captured before logging was disabled, it doesn't retroactively collect anything. Call enable_logs(node_id, workload_id, True) first if you need a fresh log stream to inspect.
Check status
workload = client.nodes.workloads.get(node.id, "<workload-id>")
info = client.nodes.workloads.get_container_info(node.id, "<workload-id>")
client.nodes.workloads.refresh_info(node.id, "<workload-id>")
get(node_id, workload_id) returns a Workload: as covered in Marketplace workload deployment, Workload.id is just the workload_id you passed in (the API doesn't return its own top-level id for a workload), and everything meaningful lives in workload.raw: status, deployed version, service state. get_container_info(node_id, workload_id) returns a raw Dict[str, Any] with the Docker-level detail shown in the workload card's Info segment: images, ports, networks, refreshed every time you expand that segment in Panel.
refresh_info(node_id, workload_id) asks the node to refresh the workload's reported info and status rather than reading a possibly stale cached value, useful right after start/stop if you want the freshest read before calling get() again. It returns None; call it, then call get() or get_container_info() immediately after to see the refreshed data.
history = client.nodes.workloads.get_status_history(node.id, "<workload-id>")
pending = client.nodes.workloads.get_pending_actions(node.id, "<workload-id>")
get_status_history(node_id, workload_id) returns the workload's command-result history: one entry per past command the node received for it, each with code/msg/timestamp/isError/inProgress/cleanErrorCode. inProgress: True marks an entry that hasn't finished yet, isError: True one that failed. get_pending_actions(node_id, workload_id) complements it by reporting only the current in-flight state, one boolean per action (update/delete/run/stop_build/persistent), rather than the full history. Reach for get_pending_actions to poll "is my last call still applying?" and get_status_history when you need the trail that led there.
Read or change its configuration
config = client.nodes.workloads.get_app_config(node.id, "<workload-id>")
client.nodes.workloads.set_app_config(node.id, "<workload-id>", config={"threshold": 5})
get_app_config(node_id, workload_id) returns the decoded config dict directly, symmetric with set_app_config(config=...); the real API response wraps this in a sent/current/status propagation envelope that tracks whether the workload has actually picked up the latest value, but the SDK unwraps it to just the config dict for convenience; use client.request(...) directly if you need that envelope. set_app_config(node_id, workload_id, *, config=None, config_id=None) takes either an inline config dict or a config_id pointing at a reusable client.configs entry; if both are given, config_id wins.
See App & global configuration for the full picture of node-level vs. workload-level vs. reusable configuration.
Read its URLs and running services
urls = client.nodes.workloads.get_urls(node.id, "<workload-id>")
services = client.nodes.workloads.get_running_services(node.id, "<workload-id>")
get_urls(node_id, workload_id) reads back the Urls section Panel shows for a deployed Marketplace app; empty for Docker/Model workloads, or a Marketplace app version that doesn't publish any. get_running_services(node_id, workload_id) reads back the running containers backing this workload, the same detail Panel uses to paint the workload card's service cards.
Remove it
client.nodes.workloads.delete(node.id, "<workload-id>")
delete(node_id, workload_id) uninstalls the workload, the equivalent of the Uninstall action in the top-right corner of the workload card's header. It returns None. There's also delete_persist(node_id, workload_id), which additionally wipes the workload's persistent folder, the same data the card's Persistent Folder segment shows and offers to delete separately.
Use plain delete if you want to keep that data around in case you redeploy the same app later, and delete_persist if you want a clean slate.
Summary
Whatever kind of workload you deployed, the same handful of methods, start, stop, logs, status, config, delete, cover its whole lifecycle from here on. At cluster scope, client.clusters.workloads exposes the identical set of methods for a cluster workload, just with a workload_id scoped to the cluster in place of a node-scoped one and a cluster_id in place of a node_id.
Managing a workload this way from a script means the same start/stop/config actions you'd otherwise click through Panel for run identically across as many workloads and nodes as your script targets.
Continue to App & global configuration for a closer look at the config side of a workload, or see client.nodes.workloads in the Reference for the full method list.