Prerequisites
Install or update to the latest published CLI by running the installer:ghcr.io/tinfoilsh/tinfoil-cli.
Check your installation with tinfoil --version.
You also need a Tinfoil organization with Tinfoil Containers enabled, the same prerequisite as the quickstart.
Updating the CLI
Check for a newer stable release or install it without logging in:Authenticating
Container management requires an organization admin API key. A personal admin key selects a personal context and cannot manage organization instances. Create an organization key by following Getting a Tinfoil admin key, then run:~/.tinfoil/config.json with mode 0600. whoami checks the effective credential with the server and displays its organization or personal context, user ID, controlplane URL, and credential source. For an organization context, it shows the returned organization ID; do not infer an organization from a key prefix or the GitHub repository owner.
These environment variables can override saved configuration:
Credential precedence is
TINFOIL_ADMIN_KEY, then an admin key in TINFOIL_API_KEY, then the saved file. Logging in saves a key but does not unset environment overrides; logging out removes only the saved file. To switch organizations, clear or change active overrides, log in with the intended organization’s key, and check tinfoil whoami again.Inspecting your organization
-o json to get machine-readable output suitable for scripting:
owner/repo) or project UUID. Instances can be referenced by name or instance UUID. If a debug-mode and a production-mode instance share a name, the CLI lists both and asks for the UUID.
Publishing a config release
Before a tag can be deployed, the config repo must publish a measured release. In the dashboard, open the repository’s config editor under Containers, edittinfoil-config.yml, open and merge the generated pull request, then follow the Release steps.
Alternatively, commit the config change in GitHub and run the Tinfoil Release workflow from the repository’s Actions tab. Wait for both release workflow phases to finish before deploying the tag. See Updating for both workflows.
The same flow is available from the CLI with tinfoil repo, which acts through the Tinfoil GitHub App installed on the repository:
repo build run; repo pr status must report Merged: true. repo build run only queues a release. repo build status checks its workflow without dispatching another run; also follow the GitHub Actions link and verify both tag creation and measurement/publication succeed. repo build info reports tags and published releases; confirm the same tag is published before creating the instance. It also suggests the next patch version. The build command accepts semver and adds the v prefix if missing. The repository must be public, so never put secret values in tinfoil-config.yml.
Creating an instance
Oncev0.0.1 is measured and published, create an instance using that exact tag. For the template, create its GREETING_TOKEN secret first:
The command follows the deployment, printing observed boot stages until the instance is Running, and exits non-zero if it observes a failure. Pass
--no-wait or -o json to return the initial response without following progress. Create also records the project and revision you typed in your connection policy, so the first connect from this machine does not prompt.
Before updating anything, send the first verified request, using the reported domain and myorg/[email protected]. Optional flags above are for applications that need them; a custom domain must already be verified, and selected secrets must already exist.
A named volume mount requires a disk only when it declares key-secret (JSON key_secret). Pass --volume for each required mount; optional mounts may remain empty. If required mounts exist and you omit --volume, create refuses before creating the instance and prints the volume create and container create --volume commands to run. See Create and attach a volume.
Initial creates cannot be held for review. Marking the tag as the repository’s latest
GitHub release defaults to true; pass --mark-latest=false to leave it
unchanged. It happens only when the GitHub App is connected, the repository is
active, and an initial or changed tag is running on a non-debug instance.
Explicit --host selection requires host-selection access for the organization,
and the selected host and GPU size must be available to it. --replace is
destructive: the old instance is stopped and removed before the replacement is
queued. If the replacement later fails, the old instance is not restored.
Managing projects
One project contains all instances in your organization that use the same GitHub repository. Inspect its aggregate status and instance counts with:container update operations, including config-only updates, and batch project update operations inherit this setting when --hold is omitted. An explicit value applies only to that operation; it does not change the project default. Creates and deploys are never held.
Update all running instances to one release:
--instance with each instance UUID:
--hold=true or --hold=false to override the project’s default for one update. Use =: a space-separated boolean is parsed as another positional argument. Holding applies only to blue/green updates (CPU-only and single-GPU instances without persistent volumes). If preflight finds an eligible instance whose target release requires replacement, a held project update is rejected before any instance is updated. Only running instances are updated; stopped and failed instances are skipped (bring them up with container deploy), as are instances with an update already in progress. Without a hold, replacement updates require downtime confirmation; pass --yes in scripts. After preflight, results are reported per instance, and the command exits non-zero if any instance is skipped or fails.
Marking the tag as the latest release defaults to true and happens for each changed tag once it is running and serving production, subject to the repository and GitHub App conditions described above. For held updates it happens after promotion. Pass --mark-latest=false to leave the latest release unchanged.
Lifecycle
An instance is either running or it is not.deploy boots an enclave for an instance that has none; update replaces the version a running instance serves. To run the same version on a fresh enclave, update a blue/green instance to its current tag (no downtime), or stop and then deploy any instance (downtime).
deploy and update support --tag, --variable, --secret, --ssh-key, --debug, --mark-latest, and --custom-domain. deploy also supports --volume and --host; update also supports --hold. Supplied variables replace the full saved per-instance variable map; they do not replace the measured env values in tinfoil-config.yml. In managed delivery, a supplied secret list replaces the saved manual selection, then merges with declared secrets that exist in repository or organization scope. Supplied SSH-key lists replace their saved selections.
--secret selects managed secrets, never private-keyserver names. A non-debug
private target requires an empty managed selection; omitting --secret does not
clear saved bindings. Pass a single --secret="" to send "secrets": [] on an
individual create, deploy, or update; do not combine it with named selections.
When switching to private delivery, also remove colliding saved variables with
a complete replacement map. See the CLI and API transition example.
Never upload private values as a workaround. In managed delivery, declared
secrets are still merged after clearing the manual selection.
A running instance cannot change hosts. Stop it, then deploy it with --host. Host access and GPU entitlement are validated when you select a host. A volume cannot automatically move to another host.
When deploying after a stop, wait until the instance is Stopped before changing its tag, host, or disk attachments. A deploy queued while it is Stopping retains the current tag, host, and disk attachments. deploy --volume must attach the disk first, so it fails rather than queueing if the instance is still Stopping.
deploy, update, and promote follow progress, printing observed boot stages and exiting non-zero if they observe a failure. A held update returns when the candidate is ready for review, without switching production traffic; other successful operations finish when the instance is Running and the update has completed. Pass --no-wait or -o json to return the initial response without following progress.
Updates and downtime
Before submitting an update, the CLI prints a read-only current-to-target plan: tags, CPU/RAM/GPU resources, strategy and downtime, effective hold and its source, changed setting names, retained volume data, and whether to mark GitHub latest. No secret values are printed. The plan is not a historical snapshot or a reservation; the server validates the update again when it is submitted. When the plan includessecret_delivery, the CLI distinguishes managed and
external secret names and states that private-keyserver authorization and unlock
are not verified. JSON output preserves this optional object and its
external_secrets_verified: false field; see the response schema.
CPU-only and single-GPU instances without persistent volumes update blue/green: the new version boots alongside the current one and traffic switches once it is running. Multi-GPU instances and instances with persistent volumes cannot run two copies, so update stops the current version first and deploys the new one in its place. The CLI describes the downtime and asks you to type yes before proceeding; pass --yes in scripts. tinfoil container get shows Updates: replace for instances whose updates cause downtime.
container promote while the current enclave serves production. Otherwise traffic switches automatically. container cancel discards the new version without changing the production-serving instance; it also clears a failed update. Updates that replace the running instance cannot be held or canceled once the current version has stopped.
Volumes
Manage encrypted persistent volumes (Beta) withtinfoil volume:
--volume to container create or container deploy to attach and deploy in one step.
Secrets
Manage organization secrets withtinfoil secret:
tinfoil repo secret with the same subcommands and flags:
--value-file accepts - for stdin, which is the recommended way to set secrets because it keeps the value out of shell history and process listings. See secrets and env vars for the underlying model.
SSH keys
Debug-mode instances authorize SSH access using public keys registered at the org level: Choose one of the supported input forms:--ssh-key NAME on tinfoil container create, deploy, or update.
Registry credentials
For private images, set credentials per registry. The CLI supportsghcr, gcr, and dockerhub; configure ECR and custom registries in the dashboard or through the Admin API.
Custom domains
tinfoil container create ... --custom-domain api.example.com. See custom domains for the DNS record details and troubleshooting.
Model weights
For GPU inference instances,tinfoil model prepares Hugging Face weights as
verified artifacts that enclaves load with integrity checking. See
Model weights for how the pieces fit together.
wrap pins the repo’s latest default-branch commit unless --commit is
given. update requires an existing completed wrap of the repo on that host
and resolves the newest commit when --commit is omitted; when an update run
with --wait completes, it prints the new models: block along with the
delete command for the previous wrap job. Run that delete only after the new
config is released and deployed. delete is refused while a job is running
or while any deployment on the host references the artifact.
Connecting to a deployed instance
Beta: Verified connections use V3 attestation, which is still being deployed. This designation applies to connection verification and V3-dependent features, rather than ordinary instance management.
tinfoil container connect <name> resolves the production connection’s domain and config reference, checks the resolved config against your local connection policy, then runs a verified local proxy pinned to that config. It saves you from looking up those values and passing them to tinfoil proxy -e <domain> -r <repo>@<tag> by hand:
tinfoil container connect my-app --review -p 3301. It pins the candidate tag and refuses if no review connection is available; it never falls back to production. container get and deployment progress output print available URLs with verified HTTP request commands. See Hold for review.
The proxy binds to 127.0.0.1 by default. Pass --bind <ADDRESS> to use another interface:
http://localhost:3301 exactly as you would to your instance’s domain. The proxy verifies attestation on startup and pins the TLS certificate for subsequent requests. See connecting for the full client story (SDKs, raw tinfoil http).
Connection policy
Inspect or configure the policy that gates these connections with thetrust
command — the file is ~/.tinfoil/known-configs.yml, yours to edit, and the
CLI preserves your comments when it writes:
Resource metrics
data_points and interval. Each data point has a timestamp, average and maximum CPU/GPU utilization, average and maximum CPU/GPU memory utilization, and CPU/GPU total memory. interval is the width of each time bucket. This is useful for piping into jq or a chart tool.
Scripting tips
-o jsonis supported on every list/get command.- Exit codes are non-zero on failure, with the API error message printed to stderr.
- All commands respect the environment overrides listed above, so the CLI is safe to use from CI. Store the admin key as a secret and pass it through the environment instead of running
tinfoil login. --verboseand--traceincrease log verbosity for debugging connectivity issues.

