my-app for a container group, ghcr.io/me/app for an image, and my-org for an
organization. Container group commands are written out as saladcloud container-groups; saladcloud cg is the same
command.
Replace image names and media URLs with resources you can access, and create any named input files before running a
command. The shell scripts and jq examples below use bash and require jq. Deployment, queued work, transcription,
and AI Gateway completions can incur charges; stop or delete temporary groups after testing.
Everyday Usage
Check Prices and Capacity Before You Deploy
availability takes the requirements of one replica: --gpu (a GPU class name or ID, repeated for several classes),
--cpu, --memory in MB, --storage in GB, and --country. Each query is expensive for the platform, so run it once
before a deployment rather than in a loop. The counts are estimates, and a row of all zeros can mean the availability
service is overloaded rather than that no nodes exist; the CLI prints a warning when that may be the case.
Deploy a Container Group
container-groups create builds a container group from flags, from a definition file, or from a copy of an existing
group.
From flags. This is the quickest way to deploy an image:
- Fields you do not set get defaults: 1 vCPU, 1024 MB of memory, 1 replica, a restart policy of
always, and the group starts as soon as it is created. --portexposes one container port through the Container Gateway. Requests to the group’s URL must then carry a SaladCloud API key in theSalad-Api-Keyheader; add--no-authto make the URL public.--waitblocks until the group isrunningand exits non-zero if it fails, stops, or does not get there before--timeout(10 minutes by default).- Words after
--replace the image’sENTRYPOINTandCMDtogether, for example-- python app.py --port 8080.
web.yaml
--from copies a group in the same organization, which is useful for trying a new image
next to the running version:
-f file under container.registry_authentication.
A deleted container group’s name is never released. Creating a group with a name that was ever used in the project
fails with
name_conflict, so give each new group a new name.Inspect Container Groups and Instances
-w to list, get, or instances list to redraw the table every few seconds until you press Ctrl-C.
A group reads running as soon as any instance’s container runs, whether or not it is passing its readiness probe yet.
instances list shows the READY state of each instance.
Update, Scale, Start, and Stop
update and scale wait for the change to settle before they return, then compare the result with what you asked for.
If the API changed or ignored part of your request, they print a warning: or note: line on stderr. If the platform
rejects the change, for example because the registry does not serve the new image tag, the command exits with code 6 and
the platform’s reason, and the group keeps running what it had.
In v0.1.0, settled does not mean rolled out or ready: the group’s configuration can be accepted while instances
still run its previous version or are starting. Inspect container-groups instances list -c my-app and test your
application before sending traffic to it. --wait on create or start waits for the group to be running, not for every
replica to pass its readiness probe. Do not use an unreleased flag from the repository’s main branch unless your
installed CLI’s --help lists it.
Three behaviors are worth knowing before you update a group that is serving traffic:
- A change to the image, command, environment variables, probes, or gateway settings restarts every instance. Each
instance downloads the new image again, and old and new versions never run side by side. To avoid downtime, deploy the
new version as a second group with
create --from, check it, and switch your clients over. See Deployment Strategies. - A new private image needs its registry credentials again. Pass them in a
-ffile together with--image. - Setting the replica count puts scheduled scaling
on hold.
scale --replicas <n> --keep-schedulesets the count and switches the schedule back on.
patch.json to restore the image’s own command:
patch.json
container.environment_variables replaces the entire set; use -e to preserve other environment variables. Only use
null where the update schema permits clearing a
field.
Read Logs and System Events
--since, a query covers the last 15 minutes. It prints the newest 1,000 entries in the range by default;
--limit changes that. --filter matches a substring, and --query takes a raw
log query.
saladcloud logs with no flags searches the whole organization. Only an explicit --project or -c <group> narrows
it; your saved default project does not.
Open a Shell or Copy Files
SSH access needs a public key registered to your account. Do this once:--instance <id>, these commands use the group’s first running instance and say which one on stderr. See
SSH and Terminal Access for what the image needs.
A one-shot
ssh <group> -- <command> exits with code 1 even when the command succeeded, because the SaladCloud relay
does not pass exit codes back. In a script, have the remote command print a line you can check for. cp exit codes
are reliable.Debug a Container That Will Not Start
Start with the system events, which show each instance being allocated, downloading the image, starting, and exiting with its exit code:sleep 2147483647 is used because sleep infinity fails in BusyBox-based images. When you are done, put the image’s
own command back by sending a patch file that contains {"container": {"command": []}}:
Job Queues, Transcription, and AI Gateway
The CLI also covers the other SaladCloud services. Each has--help with the full set of flags.
- A queue connection is set when a group is created and cannot be changed later. The image must run the
job queue worker next to your application. Give the
group a readiness probe in a
-ffile as well, so that the worker takes jobs only once your server is listening. inference transcribeuploads a local file to S4 and deletes the upload once the transcript is back. Transcription is billed by audio length; see Transcription. A failed or timed-out job can leave the upload in S4; read the diagnostic for its path and clean it up once no job needs it.--no-waitand--keep-uploadalso retain the file. Do not share signed media URLs.ai-gateway loginandai-gateway testeach send one billed request to check the key. The AI Gateway uses its own key, not your SaladCloud API key.
Automation
Everything above works unattended. A script or pipeline needs three things from the CLI: credentials that do not come from a prompt, output it can parse, and a reliable signal of success or failure.Authenticate Without Logging In
Set these environment variables instead of runningsaladcloud login:
An environment variable wins over the value saved in the config file, and a flag (
--org, --project) wins over both.
Scope flags are accepted only where applicable: inference uses --org, not --project, and ai-gateway accepts
neither. AI Gateway billing follows its key’s organization. A saved project is ignored when you select a different
organization; set the project explicitly for that organization.
- Always set
SALAD_PROJECT. When nothing sets a project, the CLI usesdefaultand prints a warning on stderr. In an organization with several projects, a script that relies on that fallback can act on the wrong one. - The CLI does not prompt when
CIis set, which GitHub Actions, GitLab CI, and most other CI systems do for you. Elsewhere, setSALAD_CLI_NO_PROMPT=1if your runner gives commands a terminal. - Store the key as a secret. It grants access to everything your user can reach, in every organization you belong to. Consider a dedicated member account for automation; the API key section of Build with AI Agents explains why and how.
saladcloud whoami shows which value is in effect for each setting and where it came from, without printing the key.
Parse JSON Output
Add-o json to any command:
- One JSON document for resource and action results. A list is
{"items": [...]}.logsprints one JSON object per line as entries arrive. SSH and SFTP sessions retain remote output;ssh --config, shell completion, and help print text instead of JSON. - Every key is always present. A list or map the API left out is empty (
[],{}), andnullmeans the value is unknown or not set. - The exit code determines success. Waiting writes (
create --wait,update,scale) withhold their result until the wait succeeds. Other commands can print output before failing:doctorprints its check results even when a check fails, and a log stream or SSH session can fail after producing output. - stderr carries
warning:andnote:lines, and the error document if the command fails. Keep the two streams apart: do not pipe2>&1into a JSON parser. - Keys are snake_case, with two exceptions on a container group that the API spells with hyphens:
scaling-actionsandscheduled-scaling-enabled. Injq, write them as.["scaling-actions"].
Handle Exit Codes and Errors
container-groups ssh, cp, and sftp are the exception: they exit with the code of the underlying ssh or sftp
process.
With -o json, a failed command prints one JSON object as the last line of stderr:
message states the problem and hint states the fix; hint is null when there is none. kind comes from a fixed
set, such as auth, not_found, invalid_input, wait_timeout, and group_failed, so a script can branch on it. The
full list is in the
CLI guide.
This snippet reads a group’s status and handles the two outcomes a deploy script usually cares about:
Check the exit code, not the output. A command with no resource to return, such as
delete, prints a document with
"ok": true, but ok is never false: a failure is always a non-zero exit code with the error document on stderr.Example: A Deploy Script
This script rolls out a new image. It creates the group from a definition file the first time it runs, and updates the image on every run after that. Save theweb.yaml definition above beside deploy.sh and run the script from that directory. Adapt the port and
readiness probe to your application, and pass a complete image reference that has already been pushed to its registry.
deploy.sh
update or create command, because that command is the last one it runs.
For example, run bash deploy.sh ghcr.io/my-org/my-app:v2 with your own image reference. A successful update verifies
the accepted configuration, not application readiness; check the instances and application afterward. If the image is in
a private registry, add a -f file with container.registry_authentication to the update command as well.
Example: GitHub Actions
This manually triggered workflow installs a pinned CLI and updates an existing group. Store the API key as a repository secret namedSALAD_API_KEY, replace the organization, project, and group names, and supply an image that
has already been built and pushed. It does not build an image or create a missing group. Private images also need
registry credentials supplied securely to the update command.
.github/workflows/deploy.yml
- GitHub Actions sets
CI, so the CLI never prompts. saladcloud doctorfails the job early, with a hint, if the key, organization, or project is wrong. A missing AI Gateway key or SSH key does not fail it.- The
updatestep fails the job if the platform rejects the new image, for example because the tag was never pushed. Success does not establish that every replica runs the new version or that the application is ready for traffic.
What Surprises Scripts
deleteis immediate and unconfirmed. There is no prompt and no dry run, so guard destructive steps in your own code.- Names are single-use. A pipeline that creates a group per run needs a unique name each time, for example with the commit SHA as a suffix. Names are 2 to 63 characters: lowercase letters, digits, and hyphens, starting with a letter.
- A wait that times out does not undo anything. The group keeps deploying on SaladCloud. Check it with
container-groups getandinstances list; do not blindly repeat a create or other write. A timed-out job submission or AI Gateway completion may also have succeeded and incurred charges. Reconcile the existing resource or job before retrying, and never automatically repeat an uncertain billed completion. -wis for people.--watchredraws a table and cannot be combined with-o json. In a script, use--wait, or pollget -o jsonyourself.update --no-waitskips verification. It returns before the change settles, so nothing checks that the API applied what you asked for.
Further Reading
- The CLI guide on GitHub documents every create flag, update and merge-patch behavior, scheduled scaling, and the complete JSON and exit code contracts.
- The notes list CLI and platform behavior that tends to surprise people.
- Use the SaladCloud CLI with AI Agents covers running the CLI from a coding agent.