name: workload description: "Primary skill for creating, updating, running, and debugging workloads on Control Plane; routes to a deeper skill per subject. Use when the user asks to deploy or run a container, app, API, service, worker, or job, or to change, scale, expose, secure, or diagnose one."
Workloads — Primary Skill & Router
Tool availability: some MCP tools named here live in the
fulltoolset profile — if one is not advertised on this connection, tell the user to reconnect the MCP server with?toolsets=full(or use thecplnCLI fallback). Reads and deletes work on every profile via the genericlist_resources/get_resource/delete_resourcetools.
A workload is Control Plane's unit of deployment: one or more containers plus how they scale, get exposed, store data, and stay healthy. This skill carries the must-know primary rules for safely creating, updating, and running a workload.
Need more detail on one subject? This skill covers the common case; for depth on a single topic, load the matching skill from the Deep-dive router at the end — you may load one or several, as the task spans. If this plugin is installed in your agent, the skill files are already available — open the relevant skill(s) directly. If you are using the Control Plane MCP server without the plugin, call get_cpln_skill with the skill name instead.
Workload type — the first decision (standard is the default)
create_workload defaults the type to standard when you don't specify one and covers all four types — serverless / standard / stateful, and cron by setting type: cron (which makes schedule required). Type is chosen at creation and is immutable (see Immutability below). Pick from:
| standard (default) | serverless | stateful | cron | |
|---|---|---|---|---|
| Use for | long-running services, APIs, workers | request/event-driven HTTP that scales on demand | databases & anything needing stable disk or per-replica identity | scheduled jobs |
| Autoscaling metrics | cpu, memory, latency, rps, multi, keda, disabled | concurrency, cpu, memory, rps, disabled | cpu, memory, latency, rps, multi, keda, disabled | n/a — runs on a schedule |
| Capacity AI | on by default | on by default | not applied | not applied |
| Probes | define readiness + liveness | define readiness + liveness | define readiness + liveness | ignored |
ext4/xfs volumes |
no | no | yes (only here) | no |
shared volumes |
yes | yes | yes | yes |
| Scale to zero | KEDA only | yes | KEDA only | n/a |
Default minScale |
1 | 1 | 1 | n/a |
The intended scaling metric can decide the type: concurrency scaling exists only on serverless — if that's the intent, create the workload as serverless (type is immutable); on standard/stateful the closest equivalent is rps. Never pair a metric with a type that rejects it.
A workload has 1–8 containers.
The spec at a glance — which tool sets what
There is ONE way to express each concept. Containers always go in the typed containers[] array (there are no flat image/cpu/port fields), scaling always goes in the single autoscaling block, and cron is create_workload / update_workload with type: cron — the schedule + job policy become available (and required), while autoscaling/capacityAI/timeoutSeconds/debug do not apply to cron and are rejected. The advanced blocks below were split into dedicated configure_workload_* tools to keep the common path lean.
| Spec block | What it controls | Set with |
|---|---|---|
containers[] — image, ports, cpu/memory, env, command/args, probes, metrics, volumes |
the container(s) — the only way to define them | create_workload / update_workload (all types, cron included) |
autoscaling (→ spec.defaultOptions.autoscaling) + capacityAI / timeoutSeconds / suspend / debug scalars |
scaling & resource optimization | create_workload / update_workload |
firewallConfig (or the public shortcut) |
inbound/outbound/internal exposure | create_workload / update_workload (all types, cron included) |
schedule + cron policy (concurrencyPolicy, historyLimit, restartPolicy, activeDeadlineSeconds) |
cron schedule & job policy | create_workload / update_workload with type: cron |
loadBalancer (direct / geo / replicaDirect) |
custom ports, static IPs, geo headers | configure_workload_load_balancer |
sidecar.envoy |
Envoy filter chain (e.g. JWT auth) | configure_workload_sidecar |
extras |
BYOK-only affinity / tolerations / topology | configure_workload_extras |
localOptions (incl. spot, multiZone, capacityAIUpdateMinutes) |
per-location overrides of defaultOptions |
configure_workload_local_options |
rolloutOptions |
graceful termination, surge/unavailable | configure_workload_rollout |
securityOptions |
runAsUser, filesystemGroupId |
configure_workload_security |
requestRetryPolicy |
request retry attempts / conditions | configure_workload_retry |
update_workload merges containers[] by name — send only the container(s) you want to change; others are preserved (an unknown name adds a container). On a cron workload, update_workload patches the schedule / job policy / suspend / containers (and rejects autoscaling/capacityAI/timeoutSeconds/debug); schedule/job fields are rejected against a non-cron workload. Always call get_resource_schema for the workload kind before authoring a spec — never hand-write fields from memory.
Production-grade defaults
Platform defaults are not a production design. For any real workload:
minScale ≥ 2for user-facing services (HA — no single point of failure). The schema default is1; use1only with a named reason (single-writer DB, leader election, dev/staging).statefulis often correct at1.maxScale: leave it at the default of 5 unless the user gives an explicit maximum. If the user says "max 10 replicas" (or names any number), set exactly that. Do not invent a different cap.- Never set
minScale: 0(scale-to-zero) unless the user asks for it by name.serverlessscales to zero directly;standard/statefulonly withmetric: keda;croncannot. - Define both
readinessProbeandlivenessProbe— none are configured by default. - Size
cpu/memoryto the runtime, not the platform defaults (50m/128Mi). Floors: CPU ≥25m, memory ≥32Mi. Keepmemory(MiB) / cpu(millicore) ≤ 8(raise to 32 with the tagcpln/relaxMemoryToCpuRatio). - Pick an autoscaling metric that fits the traffic shape (see Autoscaling).
- Set the firewall to match intended exposure IN THE CREATE CALL — it is deny-by-default (see Networking). Decide reachability before creating (
public: trueorfirewallConfig); creating closed and patching the firewall open afterward is a spec error, not a workflow. - Never silently downgrade an incompatible request to
disabled/none/1/ public — surface the conflict with realistic alternatives and a recommendation.
Images
- Your org's private registry, in a spec:
//image/NAME:TAG(e.g.//image/api:v1.0) — the preferred form. - Another Control Plane org's registry:
OTHER-ORG.registry.cpln.io/NAME:TAG— this hostname form is valid in a workload spec for cross-org pulls. - Public images: the exact string (
nginx:latest) — never add adocker.io/prefix. ECR/GCR/etc. use their full host path. - The
<your-org>.registry.cpln.io/NAME:TAGform also resolves, but for your own org prefer//image/NAME:TAG; the hostname form is mainly used bydocker login/docker push. - All images must be
linux/amd64— a wrong-arch image fails withexec format error. - Private external registries need a pull secret on the GVC (
spec.pullSecretLinks); onlydocker,ecr, andgcpsecret types work as pull secrets. Same-org//image/...needs none. - Building and pushing is CLI-only (
cpln image build --push); over MCP, images are list/get/delete only (mcp__cpln__list_resources/mcp__cpln__get_resource/mcp__cpln__delete_resource, kind="image").
Run real images
Run an actual container image — not an inline/base64/heredoc app on a generic base image. For databases, caches, queues, brokers, search, gateways, or other common infrastructure, install a Template Catalog entry first (mcp__cpln__browse_templates → mcp__cpln__install_template) rather than hand-building.
Health, readiness & verification
readinessProbegates traffic — the load balancer only routes to a ready replica. It should check request-path dependencies (DB, auth, cache).livenessProberestarts a hung process — it must check only the process itself, never downstream dependencies (a dependency outage must not cycle every replica).- Each probe is exactly one of
exec/grpc/tcpSocket/httpGet. TuneinitialDelaySecondsto real cold-start time (readiness default 10s, liveness default 60s;periodSecondsdefault 10s). - Verify every create/update automatically — without asking: poll
mcp__cpln__list_deploymentsuntil all locations report ready (it surfaces per-location errors and the workload's canonical public URL). Then give the user that canonical URL — never construct one or report a per-location deployment URL as the address. For a public workload, do not stop at "ready" — confirm it actually serves: make a real HTTP GET of the canonical endpoint (when you have that capability) and read the result — never claim reachability without a real response you received; if you cannot make a request, report readiness confirmed but external reachability not independently verified. A ready deployment can still be unreachable — firewall inbound unset, or TLS/DNS still propagating. Treat 2xx/3xx/401/403 as serving; a timeout/refused points first at firewall inbound, a TLS/DNS error at propagation (wait, don't redeploy). On failure, diagnose withmcp__cpln__get_workload_events(probe/scheduling reasons) thenmcp__cpln__get_workload_logs(app error); pass the optionallocationtolist_deployments(e.g.aws-us-east-1) to inspect ONE location's deployment in full detail. Never re-apply an unchanged failing spec, and don't poll in a tight loop.
Autoscaling & capacity
Set via spec.defaultOptions.autoscaling.metric; the system keeps the metric near but below target (default 95; capped at 100 for cpu/memory). If metric is omitted, serverless defaults to concurrency and standard/stateful default to cpu. Picker:
- concurrency — HTTP with variable request duration (serverless only).
- rps — HTTP with consistent response times.
- cpu / memory — compute- or memory-bound work.
- latency — SLO-driven APIs (standard / stateful; set
metricPercentile). - multi — several signals, highest replica count wins (standard / stateful; entries limited to
cpu/memory/rps; mutually exclusive withmetric/target). - keda — event-driven (queues/streams; standard / stateful); requires
spec.keda.enabled: trueon the GVC;targetis rejected withkeda. - disabled — fixed replicas at
minScale.
The metric must be valid for the workload type (the matrix above) or the spec is rejected — e.g. concurrency on a standard workload is rejected (it is serverless-only). Match the metric to the workload's traffic shape: rps/concurrency for HTTP, cpu/memory for compute-bound work, latency for SLO-driven APIs. For tuning targets/percentiles, multi-metric, KEDA, scale-to-zero, or Capacity AI, load the autoscaling-capacity skill.
Capacity AI auto-tunes CPU/memory between minCpu/minMemory and cpu/memory. On by default for standard and serverless; not applied to stateful or cron. It is rejected with the cpu metric (when explicitly enabled) and with GPUs.
Networking, firewall & exposure
- Deny-by-default: external inbound, external outbound, and internal (
inboundAllowType: none) are all blocked until configured. Blocked CIDRs beat allowed; CIDR rules beat hostname rules. - Public exposure needs BOTH an external inbound and an external outbound CIDR — one without the other ships a half-broken workload. Infer intent: a user-facing app/site/game → public; an internal API/DB/worker → restricted. Confirm when ambiguous or sensitive — and decide BEFORE creating: exposure belongs in the create call itself, never a follow-up firewall patch.
- Hostname outbound rules allow only ports 80/443/445 by default;
outboundAllowPortreplaces that set (re-list 80/443 if still needed). Private RFC1918/CGNAT ranges inoutboundAllowCIDRare silently ignored on managed locations — reaching private networks takes a wormhole agent (native-networking). - Internal service-to-service uses plain HTTP over the internal hostname:
http://WORKLOAD.GVC.cpln.local:PORT(the sidecar adds mTLS — neverhttps://). Same-GVC is free; cross-GVC needsinboundAllowType: same-org(or an explicitworkload-list) and incurs egress. - One public canonical port.
WORKLOAD.GVC.cpln.appserves a single port — the first container port.standard/statefulmay expose more ports across containers (unique numbers), reachable atWORKLOAD.GVC.cpln.local:PORTor via a direct/dedicated load balancer;serverlessis limited to one container / one port.WORKLOAD.GVC.cpln.appis the URL shape only — always report the actual canonical URL fromlist_deployments/ the workload'sstatus.canonicalEndpoint; never construct or guess it (custom domains, BYOK, and alias suffixes make the literal form wrong). - Always declare ports with the
containers[].portsarray — e.g.ports: [{ number: 80, protocol: "http" }]; for a single port use a one-element array. The legacy scalarcontainers[].portfield is deprecated — never use it, even ifget_resource_schemastill lists it (the platform keeps it for backward compatibility, but new specs must useports[]). - Load balancer picker: shared (default, HTTP/HTTPS on 80/443, no config) · direct — per-workload custom TCP/UDP
externalPort22–32768, optional static IPs via an IP set, geo headers; set withconfigure_workload_load_balancer· dedicated — per-GVC custom domains and wildcard hosts; a GVC setting, enabled withupdate_gvc.firewallConfigstays oncreate_workload/update_workload. Toggling direct/dedicated needs theconfigureLoadBalancerpermission —editdoes not imply it (ipset-load-balancingskill).
Persistent storage
- Need durable disk or stable per-replica identity → a
statefulworkload with a mounted volume set. - A volume set's filesystem (
ext4/xfs/shared) and performance class are immutable — set at creation. ext4/xfsmount on stateful only;sharedmounts on any type. Up to 15 volumes per container, and no two mounts in a container may share a path or nest (one mount path cannot be a parent of another). Reserved mount paths (rejected):/dev,/dev/log,/tmp,/var,/var/log.- Snapshot before any destructive volume op (shrink/restore/delete); snapshots exist for
ext4/xfsonly. - Attach with
mcp__cpln__mount_volumeset_to_workload.
Secrets, env vars & naming rules
- Secrets can be consumed two ways: as an environment variable value —
cpln://secret/NAME(orcpln://secret/NAME.keyfor a keyed/dictionary secret) — or mounted as a volume withuri: cpln://secret/NAME. Either way the workload still needs all three pieces: an identity on the workload, a policy grantingreveal, and the reference — or access fails silently.mcp__cpln__workload_reveal_secretsets the identity + policy but not the reference, and it requires the workload to already exist — for a new workload,create_workloadfirst (its deployment pauses on the secret reference until access is granted, then resumes); never callworkload_reveal_secretbefore the workload exists. - Environment variable names cannot start with
CPLN_(reserved). The platform injects these at runtime:CPLN_TOKEN,CPLN_ENDPOINT,CPLN_GLOBAL_ENDPOINT,CPLN_ORG,CPLN_GVC,CPLN_GVC_ALIAS,CPLN_LOCATION,CPLN_PROVIDER,CPLN_WORKLOAD,CPLN_WORKLOAD_VERSION,CPLN_IMAGE,CPLN_NAME(plusCPLN_MAINon the first container, andPORTon standard when unset).K_SERVICE/K_CONFIGURATION/K_REVISIONare also disallowed. Names match^[-._a-zA-Z][-._a-zA-Z0-9]*$(max 120 chars). - A workload can call the Control Plane API as its identity:
curl -H "Authorization: Bearer $CPLN_TOKEN" $CPLN_ENDPOINT/org/$CPLN_ORG/...—CPLN_ENDPOINTis plain http (the sidecar secures and signs it in transit). Requests act as the attachedspec.identityLinkidentity and succeed only where a policy grants that identity the permission — no identity attached or no policy means 403. The token works only from inside that workload, againstCPLN_ENDPOINT: it does not authenticate toapi.cpln.io,metrics.cpln.io, orlogs.cpln.io(use a service-account key there). - Container names cannot start with
cpln-ordebugger-(and a few exact names likeistio-proxyare reserved). Names are lowercase^[a-z]([-a-z0-9])*[a-z0-9]$, max 64. - Workload name is max 49 characters, cannot end with
-headless, and is immutable.
Runtime traps
- Graceful shutdown: the default
preStoprunssh -c "sleep N". Minimal/distroless images often lacksleep— if it (or a custompreStop) fails in any container, all containers are SIGKILL'd immediately. Grace period isspec.rolloutOptions.terminationGracePeriodSeconds(0–900, default 90). - Reserved container ports (rejected):
8012, 8022, 9090, 9091, 15000, 15001, 15006, 15020, 15021, 15090, 41000. Valid container port range is 80–65535; port numbers must be unique across all containers. A serverless workload must expose exactly one port, on exactly one container. Declare every port in thecontainers[].portsarray ([{ number, protocol }]) — the scalarcontainers[].portfield is deprecated; do not use it. - Don't run as UID 1337 — that is the mesh proxy's UID. A container with
runAsUser: 1337has its outbound traffic excluded from the Envoy sidecar redirect, so it bypasses the mesh — losing mTLS and firewall enforcement (it gets unfiltered egress, not "no networking").
Immutability & destructive changes
- Workload
typeandnameare immutable. Changing either = delete + recreate, which is destructive: it drops the public URLWORKLOAD.GVC.cpln.app, the internal DNSWORKLOAD.GVC.cpln.local, and policytargetLinks/ identity bindings. Recreating with the same name preserves the URL/DNS; a different name silently breaks every external reference. - The same applies to a volume set's filesystem and performance class.
- Before any delete or immutable-forcing change, present Action · Affected · Blast radius · Data / Traffic / Access impact · Reversibility · Mitigation and wait for explicit confirmation (see the root rules).
Metrics & observability
- Built-in metrics (CPU/memory reserved-vs-used, request rate/latency, replica count, restarts) exist for every workload with no config.
- Custom Prometheus: add
spec.containers[].metricswithport(required) andpath(default/metrics). - Query with
mcp__cpln__list_metrics(discover real names/labels) →mcp__cpln__query_metrics(PromQL). Confirm a signal exists before changing scaling.
Running commands in a live replica
mcp__cpln__list_workload_replicas → mcp__cpln__workload_exec runs ONE command in a replica. It is the highest-risk tool: audited, and it hits a replica serving live traffic.
- Read-only diagnostics (
ls,cat,env,df,curl localhost) are fine. - Any state-changing command (writes, restarts, signals, installs, migrations) needs explicit user confirmation first — state the exact command, what it changes, and the risk.
- One-shot only — no interactive shells/TTYs/REPLs (use the CLI
cpln workload execfor those). - If your MCP client's safety layer blocks an approved exec (some clients block destructive, credential-bearing, or shell-pipe commands — e.g. a
pg_dump | psqlrestore — regardless of your approval, before the call reaches Control Plane), run the identical command viacpln workload execfrom a trusted shell. The platform enforces the same authorization; only the client differs. This is an expected client limitation, not a Control Plane error — report it plainly and offer the CLI command rather than retrying the blocked tool.
Standard create / update flow
- Read this skill once per session before authoring (you are doing that now);
mcp__cpln__get_cpln_ruleshas the cross-cutting operating guide if you have not read it this session. - Confirm the target org / GVC — never guess; on not-found, stop and ask.
mcp__cpln__get_resource_schemafor the workload kind before authoring.- Discover current state:
mcp__cpln__list_resources(kind="workload") /mcp__cpln__get_resource(kind="workload"). - Prepare the smallest valid change; if destructive, confirm.
mcp__cpln__create_workload/mcp__cpln__update_workload(for a scheduled job, passtype: cronwith aschedule; PATCH — only sent fields change, containers merged by name), plusconfigure_workload_*for load balancer / sidecar / extras / local options / rollout / security / retry.- Verify automatically — do not ask permission: poll
mcp__cpln__list_deploymentsuntil every location is ready; on failure diagnose with events → logs and fix. - Report exactly what changed and the resulting status — and for an exposed workload, give the user its canonical public URL (read from
list_deploymentsor the workload'sstatus.canonicalEndpoint; never construct/guess it or report a per-location URL as the address).
Quick reference — MCP tools
| Tool | Purpose |
|---|---|
mcp__cpln__create_workload |
Create any workload (typed containers[], single autoscaling block) — including a scheduled job with type: cron + a required schedule. |
mcp__cpln__update_workload |
Update a workload (PATCH; containers merged by name) — on a cron workload, patches schedule / job policy / suspend. |
mcp__cpln__get_resource (kind="workload") / mcp__cpln__list_resources (kind="workload") |
Read one / list in a GVC (capture state before changes). |
mcp__cpln__delete_resource (kind="workload") |
Delete a workload (destructive — confirm blast radius first). |
mcp__cpln__configure_workload_load_balancer |
Set/clear spec.loadBalancer (direct, geo headers, replicaDirect). |
mcp__cpln__configure_workload_sidecar |
Set/clear spec.sidecar.envoy (Envoy filters, JWT auth). |
mcp__cpln__configure_workload_extras |
Set/clear spec.extras (BYOK affinity/tolerations/topology). |
mcp__cpln__configure_workload_local_options |
Set/clear spec.localOptions (per-location overrides). |
mcp__cpln__configure_workload_rollout |
Set/clear spec.rolloutOptions (graceful termination, surge/unavailable). |
mcp__cpln__configure_workload_security |
Set/clear spec.securityOptions (runAsUser, filesystemGroupId). |
mcp__cpln__configure_workload_retry |
Set/clear spec.requestRetryPolicy (retry attempts/conditions). |
mcp__cpln__list_deployments |
PRIMARY post-deploy readiness monitor (all locations); per-location errors and the canonical public URL to report. Pass the optional location (e.g. aws-us-east-1) for ONE deployment's full detail — version chain, per-container readiness, full JSON. |
mcp__cpln__get_workload_events |
Probe/scheduling failures after a bad deploy. |
mcp__cpln__get_workload_logs |
App-side logs (LogQL) for runtime/startup errors. |
mcp__cpln__list_workload_replicas → mcp__cpln__workload_exec |
List replicas, then run one command in one. |
mcp__cpln__workload_start_cron |
Trigger an out-of-band run of a cron workload. |
mcp__cpln__workload_reveal_secret |
Grant an existing workload secret access (identity + reveal policy; you still add the reference — create the workload first). |
mcp__cpln__mount_volumeset_to_workload |
Attach a volume set to a stateful workload. |
CLI fallback (read the cpln skill first): use when MCP is unavailable/unauthenticated, for interactive work (cpln workload exec, cpln workload connect, port-forward), image build/copy, or as the primary interface in CI/CD (CPLN_TOKEN + cpln apply --ready).
Raw API escape hatch: for a spec field no typed create_workload / update_workload / configure_workload_* tool exposes, use mcp__cpln__cpln_api_request (raw GET/POST/PATCH/DELETE; disabled by default — only when advertised) — call mcp__cpln__get_resource_schema first for the exact path and body, and prefer the typed tools whenever they cover the field. If it is not advertised, apply the full manifest with the cpln CLI instead.
Deep-dive router
Load the matching skill (one or several) when you need more than the primary rules above — open it directly if this plugin is installed, otherwise fetch it with get_cpln_skill:
| Need | Skill |
|---|---|
| Image refs, builds, buildpacks, registries, pull secrets, cross-org sharing | image |
| Autoscaling, Capacity AI, scale-to-zero, KEDA, custom-metric scaling | autoscaling-capacity |
| Probes in depth, JWT/Envoy auth, security options, graceful termination | workload-security |
| Firewall rules, inbound/outbound, header & geo filtering | firewall-networking |
| Static IPs, direct & dedicated load balancers, custom ports | ipset-load-balancing |
| CDN caching, request rate limiting, DDoS protection | cdn-rate-limiting |
| Volumes, volume sets, snapshots, persistence, expansion | stateful-storage |
| Metrics, PromQL, Grafana, Prometheus federation | metrics-observability |
| Logs, LogQL, events, per-execution cron logs | logql-observability |
| Private networking, agents, VPC, on-prem connectivity | native-networking |
| Databases, caches, queues, brokers, common infra | template-catalog |
| Secrets, identities, policies, RBAC, service accounts | access-control |