# How to start a sandbox from a private registry image Store the registry's credential once with `runtime images registry set`, then build from the private image and start sandboxes from it. **On Runtime, the credential is sealed to the servers as soon as it arrives. No call returns it, and the API that stored it cannot read it back.** A build opens it only to pull, inside its own build machine. Neither your sandboxes nor your agent ever hold the registry token. Docker Hub, GitHub, Google, Amazon ECR, and any other registry that takes a user name and token work ([private registries](/docs/images#private-registries), checked 26 October 2026). ## Store the credential Each CLI form reads the secret from standard input. The secret never appears in your shell history or on a command line. ```bash no-run echo "$GHCR_TOKEN" | runtime images registry set ghcr.io --username my-user echo "$AWS_SECRET_ACCESS_KEY" | runtime images registry set 123456789012.dkr.ecr.us-east-1.amazonaws.com --access-key-id AKIA... runtime images registry ls ``` From code, keep the token in your own secret store and pass it through an environment variable: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.registries.set({ registry: "ghcr.io", username: "my-user", password: process.env.GHCR_TOKEN ?? "", }); console.log(await runtime.images.registries.list()); // never the token ``` ```python check import os from withruntime import Runtime runtime = Runtime() runtime.images.registries.set("ghcr.io", username="my-user", password=os.environ.get("GHCR_TOKEN", "")) print(runtime.images.registries.list()) ``` ## What each registry needs | Registry | Give | | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | Docker Hub (`docker.io`) | Your user name and an access token | | GitHub (`ghcr.io`) | Your user name and a token that can read packages | | Google (`gcr.io`, `*-docker.pkg.dev`) | `_json_key` and the service account key's JSON, or `oauth2accesstoken` and an access token | | Amazon ECR (`.dkr.ecr..amazonaws.com`) | An access key ID and secret that allow `ecr:GetAuthorizationToken` and pull. Each build requests a fresh token from ECR | | Any other | A user name and a token or password | An account can hold up to 20 registry credentials. To remove one, call `runtime.images.registries.delete(registry)`. ## Build from the private image A sandbox starts from a Runtime image. So the private image goes through one build first. Use it as is: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "app", image: "ghcr.io/acme/app:1.4.2" }); await using sbx = await runtime.sandboxes.create({ image: "app" }); console.log((await sbx.exec("cat /etc/os-release")).stdout); ``` ```python check from withruntime import Runtime runtime = Runtime() runtime.images.build(name="app", image="ghcr.io/acme/app:1.4.2") with runtime.sandboxes.create(image="app") as sbx: print(sbx.exec("cat /etc/os-release").stdout) ``` ```bash no-run runtime images build --from ghcr.io/acme/app:1.4.2 --name app runtime sandboxes create --image app ``` The same stored credential covers `FROM` in a [Dockerfile](/how-to/build-an-image-from-a-dockerfile), `COPY --from=`, and a [recipe's](/how-to/build-an-image-from-a-recipe) `base`. So a private base image with a few packages added on top takes one build: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "app-tools", recipe: { base: "ghcr.io/acme/app:1.4.2", apt: ["jq"], pip: ["pytest"] }, }); ``` A program in the sandbox is found on the image's own `PATH`. So `python` in an image built from `python:3.12-slim` runs as it does there. ## Keep it current Each build of a name becomes its next version. It takes `latest`, or the tags you give. When the registry publishes 1.4.3, build again with the same `name`. Sandboxes that ask for `app` get the new version. Sandboxes already running keep theirs. To pin a version, use `app@2` or a tag such as `app:prod` ([names, versions and tags](/docs/images#names-versions-and-tags)). ## Mistakes to avoid - **A GitHub token without package read access.** The pull is refused. Make a token that can read packages. - **Putting the token in a Dockerfile or build argument.** It would be in the image. Store it as a registry credential, which a build opens only to pull. - **An ECR login token instead of an access key.** Give an access key ID and secret that may call `ecr:GetAuthorizationToken`. Each build requests a fresh token from ECR itself. - **Giving the sandbox registry access to pull at run time.** It does not need it, because the image is already built. If code in the sandbox must call a private API, use a [secret it never sees](/docs/security#secrets-sandboxes-never-see) scoped to that host. Building is free. Without credit, a build counts toward the free usage. A stored image costs $0.08 per decimal GB per 30-day month, based on its accounted storage. An account without credit keeps its first three images free ([pricing](/docs/pricing#snapshots-images-and-volumes)). For containers that the sandbox runs itself, see [Docker in a sandbox](/how-to/run-docker-in-a-sandbox). A stored image is charged for its whole file ([image storage pricing](/docs/pricing#snapshots-images-and-volumes)). Facts on this page were checked on 26 October 2026.