sha256:2bee7c7ee477a349645f6cf9ae9f29636f30c0ce79ba604ab6112f061ea76b3f
Last pushed
12 days by longnguyen58445
Type
Sandbox Kit
Manifest digest
sha256:2bee7c7ee477a349645f6cf9ae9f29636f30c0ce79ba604ab6112f061ea76b3f
schemaVersion: "2"
kind: mixin
name: offload
displayName: Docker Offload
description: Installs the Docker Offload CLI plugin (v0.6.85, linux amd64 and arm64, from a plugin image) set to the Cloud Sandboxes engine (Offload v2), so agents can run Docker workloads on a remote engine through the docker-cloud context. Signs in with a Docker personal access token bound on the host.
args:
pluginImage:
default: longnguyen58445/offload-plugin:0.6.85
description: Image holding the docker-offload plugin at /docker-offload (built from kits/offload-plugin). Its binary must match the checksums pinned in the startup script.
pattern: ^[a-z0-9][a-z0-9._/:@-]*$
stage:
default: ""
description: Set to 1 to use Docker's staging environment (needs a staging PAT). Empty is production.
pattern: ^1?$
username:
default: ""
description: Docker ID the personal access token belongs to. Leave empty to sign in by hand with docker login.
pattern: ^[a-z0-9._-]*$
agentInstructions:
content: |
## Docker Offload
The `docker offload` CLI plugin is installed and set to the Cloud
Sandboxes engine. It runs Docker workloads on a remote engine (a Cloud
Sandbox) instead of this sandbox's own Docker daemon. Use it for heavy
builds, large images, or GPU work; it costs money while the remote engine
runs, so stop it when you are done.
docker offload status # signed in? engine running?
docker offload create # start a remote engine (minutes on first create)
docker offload create --cpus 8 --memory 16g
docker build . # now runs on the remote engine
docker run --rm alpine uname -a
docker offload stop # park it (keeps containers, stops billing for compute)
docker offload rm # delete it and everything on it
`docker offload create` makes `docker-cloud` the current docker context,
so every docker command after it runs on the remote engine. Switch back
to this sandbox's own daemon with `docker context use default`, or target
one engine per command with `--context default` / `--context docker-cloud`.
Sign-in: the daemon uses the Docker login in ~/.docker/config.json, read
when the daemon starts. If `docker offload status` shows no account, ask
the user to run `docker login` (or bind a PAT with
`sbx secret set docker-offload`); after a login, run
`docker offload daemon stop` so the next command starts a daemon that
sees it.
Startup output is in /tmp/offload-start.log and the daemon log in
~/.docker/cloud/logs/cloud-daemon.log.
permissions:
network:
allow:
- hub.docker.com:443
- login.docker.com:443
- index.docker.io:443
- auth.docker.io:443
- registry-1.docker.io:443
- production.cloudfront.docker.com:443
- production.cloudflare.docker.com:443
- api.sandboxes-cloud.docker.com:443
- ssh.sandboxes-cloud.docker.com:22
- 100.22.208.130:22
- 52.39.3.133:22
- 54.212.241.121:22
- api.offload.docker.com:443
- hub-stage.docker.com:443
- login-stage.docker.com:443
- index-stage.docker.io:443
- api.sandboxes-cloud-stage.docker.com:443
credentials:
- service: docker-offload
description: Docker personal access token for the account that has Cloud Sandboxes access. The startup script signs in with it, so it is present in the sandbox (and in ~/.docker/config.json); scope it narrowly.
apiKey:
name: DOCKER_OFFLOAD_PAT
environment:
variables:
DOCKER_OFFLOAD_STAGE: ${{ kit.args.stage }}
DOCKER_OFFLOAD_USER: ${{ kit.args.username }}
OFFLOAD_PLUGIN_IMAGE: ${{ kit.args.pluginImage }}
setup:
startup:
- command:
- sh
- -c
- nohup /home/agent/.local/bin/offload-start.sh > /tmp/offload-start.log 2>&1 &
user: "1000"
description: Install the plugin from its image, sign in to Docker when a PAT is bound, then start the Offload daemon in the background
files:
- path: /home/agent/.docker/cloud/config-override.toml
content: |
# Selects the Cloud Sandboxes engine (Offload v2). Without Docker
# Desktop there is no feature flag to read, and the daemon would
# otherwise fall back to the leased engine (Offload v1).
[features]
cloud_sandbox_engine = true
mode: "0644"
description: Select the Cloud Sandboxes engine
- path: /home/agent/.docker/cloud-stage/config-override.toml
content: |
# The same, for staging (stage=1), whose daemon keeps its state here.
[features]
cloud_sandbox_engine = true
mode: "0644"
description: Select the Cloud Sandboxes engine in staging
- path: /home/agent/.local/bin/offload-start.sh
content: |
#!/bin/sh
# Installs the plugin from its image, signs in with the bound PAT, if
# any, then starts the daemon. Any docker offload command would start
# the daemon too; starting it here just makes the first one faster.
set -u
image="${OFFLOAD_PLUGIN_IMAGE}"
plugin=/home/agent/.docker/cli-plugins/docker-offload
case "$(uname -m)" in
x86_64|amd64) sha=0f4f346fb1faad059e841451e274b0a7fcf8a741038f3e5563cce5030b19c235 ;;
aarch64|arm64) sha=c973a45b639c61b22c6568a644a8cdbcf74409e2c574b0f8907fcdc87a84eff6 ;;
*) echo "offload: unsupported arch $(uname -m) (expected amd64 or arm64)" >&2; exit 1 ;;
esac
# Always this sandbox's own daemon: after a create, the current
# context is the remote engine.
d() { docker --context default "$@"; }
# The sandbox's docker may still be starting.
i=0
until d info >/dev/null 2>&1; do
i=$((i + 1))
if [ "$i" -ge 60 ]; then echo "offload: docker did not come up" >&2; exit 1; fi
sleep 2
done
if ! echo "$sha $plugin" | sha256sum -c - >/dev/null 2>&1; then
# Retried: at sandbox start the pull can be refused (403) for a
# while before it goes through. A copy already here will do if the
# registry cannot be reached at all.
i=0
until d pull -q "$image"; do
i=$((i + 1))
if [ "$i" -ge 12 ]; then
d image inspect "$image" >/dev/null 2>&1 ||
{ echo "offload: could not pull $image" >&2; exit 1; }
break
fi
sleep 10
done
# A scratch image has no command; one is given so it can be created.
ctr=$(d create "$image" /docker-offload) || exit 1
mkdir -p "$(dirname "$plugin")"
d cp "$ctr:/docker-offload" "$plugin.new"
d rm "$ctr" >/dev/null
# The image tag can move; the binary must be the pinned one.
if ! echo "$sha $plugin.new" | sha256sum -c -; then
echo "offload: $image does not hold the pinned plugin" >&2
rm -f "$plugin.new"
exit 1
fi
chmod 0755 "$plugin.new" && mv "$plugin.new" "$plugin"
fi
docker offload version
# Staging reads its login from the stage registry's entry.
registry=
[ -n "${DOCKER_OFFLOAD_STAGE:-}" ] && registry=index-stage.docker.io
if [ -n "${DOCKER_OFFLOAD_PAT:-}" ] && [ -n "${DOCKER_OFFLOAD_USER:-}" ]; then
if printf '%s' "$DOCKER_OFFLOAD_PAT" | docker login -u "$DOCKER_OFFLOAD_USER" --password-stdin $registry; then
echo "offload: signed in to Docker as $DOCKER_OFFLOAD_USER"
else
echo "offload: docker login failed; sign in by hand with docker login" >&2
fi
else
echo "offload: no PAT or username bound; sign in by hand with docker login"
fi
# The daemon reads ~/.docker/config.json once, when it starts, so one
# started before a login never sees it. Start it only when a login is
# already there; otherwise the first docker offload command after
# docker login starts it.
if grep -q "${registry:-index.docker.io}" /home/agent/.docker/config.json 2>/dev/null; then
exec docker offload daemon start
fi
echo "offload: not signed in yet; the daemon starts on the first docker offload command after docker login"
mode: "0755"
description: Startup script that signs in and starts the Offload daemon
- path: /home/agent/.claude/skills/docker-offload/SKILL.md
content: |
---
name: docker-offload
description: Run Docker builds and containers on a remote Docker Offload engine (a Cloud Sandbox) instead of this sandbox's own Docker daemon. Use when a build or workload is too heavy for the sandbox, needs more CPU or memory, or when the user asks to use Docker Offload. Covers checking sign-in, creating the engine, switching contexts, verifying it works, and cleaning up so it does not keep billing.
---
# Docker Offload
The remote engine bills while it runs. Create it only when the work
needs it, and stop or remove it when the work is done.
## 1. Check before creating
```sh
docker offload status
```
- `Account:` shows a Docker ID: signed in. Continue.
- `Account: N/A`: not signed in. Do not try to sign in with a password
or token you were not given. Tell the user to bind a PAT
(`sbx secret set docker-offload` on the host, then restart the
sandbox) or to run `docker login` themselves. Stop here.
- `docker: unknown command: docker offload`: the plugin is missing;
the kit's install did not run. Tell the user.
- The status shows a running sandbox engine already: reuse it, skip
to step 3.
## 2. Create
```sh
docker offload create # default size
docker offload create --cpus 8 --memory 16g # bigger, if the work needs it
```
The first create takes a few minutes. It ends with `✓ Sandbox ready.`
and a `Size:` line. If the user asked for a size and the sandbox
already exists at another size, `create` asks before recreating it.
Recreating deletes everything on it, so do not pass `--recreate`
unless the user agreed to lose its containers.
## 3. Use it
`create` makes `docker-cloud` the current context, so plain docker
commands now run remotely. Confirm before running real work:
```sh
docker context show # docker-cloud
docker run --rm alpine uname -a # runs on the remote engine
```
The engine pauses after 5 idle minutes (`--idle-timeout` sets it,
`--keep-alive` turns pausing off). A docker command that needs it,
such as `run`, `start` or `attach`, resumes it; that first command
can take a few minutes, which is not a hang. Attached sessions are
cut off by a pause; reattach with `docker attach`.
To use this sandbox's own daemon for a command, pass
`--context default`. Published ports (`-p 8080:80`) are forwarded to
this sandbox, so `curl localhost:8080` reaches the remote container.
## 4. Clean up
When the work is done, unless the user wants the engine kept:
```sh
docker offload stop # parks it; containers and images are kept
docker context use default # back to this sandbox's daemon
```
Use `docker offload rm` only when the user wants the engine and
everything on it deleted.
## Troubleshooting
Startup output is in `/tmp/offload-start.log`; the daemon log is
`~/.docker/cloud/logs/cloud-daemon.log` (`cloud-stage` in staging).
| Symptom | Meaning | What to do |
|---|---|---|
| `create` and `rm` missing from `docker offload --help` | the Offload v2 override file is missing | check `~/.docker/cloud/config-override.toml` has `cloud_sandbox_engine = true` |
| `sign in to continue using Docker Offload` right after a successful `docker login` | the daemon was already running and only reads the login when it starts | `docker offload daemon stop`, then run the command again (it starts a fresh daemon) |
| `Account: N/A` after a bound PAT | `docker login` failed at startup | read `/tmp/offload-start.log`; the PAT may be expired or for another account |
| `create` says the account has no access | the account is not entitled to Cloud Sandboxes | tell the user; nothing to fix in the sandbox |
| HTTP 403 naming a host | the sandbox network policy blocks it | tell the user which host; they allow it on the host with `sbx policy allow network <host>` |
| `ssh: handshake failed: EOF`, repeating every 10s in the daemon log | the policy blocks SSH to the gateway; SSH has no host name, so only an `IP:22` rule matches | ask the user for `sbx policy log` on the host and an allow for each blocked `IP:22`; stop the engine (`docker offload stop`) while it cannot connect |
| `still pausing from the last stop` | the engine is still parking | wait a minute and run `create` again |
| a warning that Desktop could not ensure the daemon | expected: there is no Docker Desktop here | ignore it |
mode: "0644"
description: Claude Code skill for using Docker Offload safely from the sandbox