Debug a kit
When a kit's install or startup command fails, sbx shows you which command
failed, in which kit, and where the full output is. This page shows how to read
that output, find the logs inside the sandbox, and fix the common causes.
Read the progress output
During sbx create and sbx run, the configure agent phase prints one row for
each kit operation: install commands, init files, file copies, environment
variables, and the startup commands it registers. A finished row shows ✓ with
the elapsed time, or ✗ with the exit code.
Each command row shows the command's description when the kit sets one.
Otherwise it shows the first 60 characters of the command. It also shows the
kit that owns the command (kit=<name>) and the user it runs as (user=<uid>).
Set description on every install and startup command so the row says what the
command is for. Keep it short: a row shows at most 60 characters of it.
capabilities:
- type: com.docker.sandbox/lifecycle@1
config:
install:
- command: apt-get update && apt-get install -y ripgrep
user: "0"
description: Install ripgrep
startup:
- command: rg --version > /tmp/rg-version
description: Record the ripgrep versionThe rows for this kit look like this:
AGENT
running 1 install command
✓ Install ripgrep (kit=my-kit, user=0, 12.4s)
view install logs: ls /var/log/sbx-kits
registering 1 startup command, run on every container start
○ Record the ripgrep version (kit=my-kit, user=agent)
view startup log: cat /var/log/sbx-kit-startup.logRead a failed install command
A failed install command prints the last 40 lines of its output under the row,
followed by a docker run line that runs the same command against the kit's
base image:
✗ Install ripgrep (kit=my-kit, user=0, exit 100)
── captured output ──
E: Failed to fetch http://deb.debian.org/debian/dists/bookworm/InRelease 403 Forbidden
E: Unable to locate package ripgrep
──────────────────────
hint: reproduce locally with:
docker run --rm -u 0 <base-image> sh -c 'apt-get update && apt-get install -y ripgrep'Copy the docker run line to iterate on the command without recreating the
sandbox each time. Change the command, run it again, and copy the working
version back into the kit. The reproduction runs in plain Docker, outside the sandbox, so the sandbox's
network policy doesn't apply. If the command works there but fails in the
sandbox, check the kit's network rules first.
Find the logs inside the sandbox
The progress output shows only the end of a failed command. The sandbox keeps the full record:
| Log | Contents |
|---|---|
/var/log/sbx-kits/<kit>/install-<n>.log | Full output of one install command. <n> is the command's position in that kit's install list, starting at 0. |
/var/log/sbx-kit-startup.log | Output of every startup command, from every container start. |
Read either log with sbx exec:
$ sbx exec my-sandbox cat /var/log/sbx-kits/my-kit/install-0.log
$ sbx exec my-sandbox cat /var/log/sbx-kit-startup.log
Startup commands run on every container start. Each one writes these lines to the startup log:
> <script>
# <kit>: <description or command>
<the command's output>
ok <script>A failing command ends with fail <script> exit=<code> instead of ok. It
also stops the startup commands after it, and the command that started the
sandbox (sbx create, sbx run, sbx exec after sbx stop, or
sbx kit add) prints a warning that points at the startup log with its last
lines. The sandbox stays usable.
After the create phase, the agent takes over the terminal. The logs in the sandbox are the record you can come back to.
Show everything with -D
Pass the global debug flag to sbx create or sbx run:
$ sbx -D create --name my-sandbox shell --kit ./my-kit
With -D, each successful install command also streams its full output under
its row, and sbx prints the startup log after the sandbox starts.
What sbx redacts
Everything a kit command prints reaches your terminal through one filter. It
removes terminal escape sequences and replaces known credential shapes, such as
GitHub, GitLab, OpenAI, Anthropic and AWS tokens, JSON Web Tokens, private keys
and Authorization headers, with a placeholder like <GITHUB_TOKEN>. The
filter protects you from a command that accidentally echoes your own secrets,
for example a token in env output or a key in a configuration dump. It is not
a defence against output written to defeat it, and it does not change a secret
you wrote into the kit's own command text. The install log inside the sandbox
keeps the raw output.
Common causes
The install command can't reach its package source
The log shows names that don't resolve, or HTTP 403 responses from the
sandbox proxy, from apt, pip, or npm. The kit's install-phase network
rules don't include the package mirror. Add it to install.allow in the
com.docker.sandbox/network-policy@1 capability. The apt mirrors, for
example, can live on a different host than the one the command names.
Two kits run apt at the same time
The log shows Could not get lock /var/lib/dpkg/lock-frontend. Another
install command, or the base image setup, holds the dpkg lock. Retry the
package command in the kit, for example with -o DPkg::Lock::Timeout=120
on apt-get.
A startup command needs a tool that didn't install
The startup log shows exec: <tool>: not found after an install command for
that tool failed. Fix the install command first. Check its
install-<n>.log, then rerun the sandbox.
The command needs root
The log shows Permission denied when the command writes to a system path or
installs a package, and the row shows user=1000. Set user: "0" on the
install command, or write to a location the agent user owns.
Next steps
- Kit authoring patterns shows setup commands in context.
- Sandbox troubleshooting covers other sandbox problems.