Applications and deploys
An application is one deployable island, defined by a compose file. A deployment is one immutable revision of it. Deploying is uploading a new revision; rolling back is activating an older one.
Create an application
POST /cli/applications
{"slug": "vpn-alice", "name": "Alice's VPN", "organization": "acme"}
organization may be omitted when exactly one of your organizations is eligible.
Slugs are lowercase letters, numbers and dashes, unique per organization, and are
what every other path here names the application by.
| Response | Means |
|---|---|
201 |
Created; the body describes it |
400 |
The slug is not a valid slug |
403 |
You are a member but not an owner or admin |
409 |
That slug already exists in that organization, or you must name one |
Upload a revision
POST /applications/{slug}/bundle
A multipart upload of the compose text plus a tar.gz per build: context. The
platform parses the compose at ingest — an unusable file is a 400 before
anything is stored — then builds the contexts, pins the images to digests, places
the revision on a context, and deploys it. See
agent-cli.md
for the wire format; the sc CLI is a reference implementation of this one call.
Only a subset of compose is acted on: image, build, command, restart,
environment, ports, deploy, x-someones. Anything else is stripped with a
warning rather than rejected, and the warnings come back with the upload. Under
x-someones: the only key is http, naming the container port that speaks HTTP
(ports). Under deploy: the
subset is narrower still — replicas and resources.limits.{cpus,memory} — and
everything else there (placement, restart_policy, update_config, mode,
labels, resources.reservations) warns by its full path.
restart: is the only spelling for what happens when a container exits, and it
decides more than the restart condition:
| Written | Deployed as | Means |
|---|---|---|
absent, always, unless-stopped |
replicated service, restart on any exit | a server |
on-failure, on-failure:N |
replicated service, restart on non-zero exit | a retry; N is ignored and retries are unbounded |
no |
swarm job, replicas completions, never restarted |
a migration or batch job — expected to exit 0 and stay exited |
unless-stopped has no swarm equivalent and warns that it was treated as
always. Anything else is a 400.
Configuration
PUT /cli/applications/{slug}/variables
{"variables": {"OVPN_SERVER_CONF": "…"}, "sensitive": true}
A map at a time, in one transaction, so a dropped connection cannot leave half an
environment applied. sensitive marks values unreadable afterwards — nothing in
the API ever loads a value back, only its metadata, which is why a secret cannot
escape through a field somebody adds later.
Variables are resolved and frozen into a revision when it deploys, so changing one affects the next deploy rather than what is running.
Watch it
Deployment is a readable resource with status and statusReason. Poll it, or
list revisions inline:
GET /cli/applications/{slug}?deployments=1
The statuses that matter to a consumer are pending → building → built →
deploying → running, with failed reachable from any of them and
statusReason carrying the sentence explaining why. A revision that landed
badly also carries failure — {"reference": "F-24GT1BQ7", "url": "…"} — which
is where the rest of the account is: the phase, whose fault it was, and the
build log. It is null on every revision that is fine. Logs stream from
GET /cli/applications/{slug}/services/{service}/logs.
Once a revision is running, ask where it answers.