Share feedback
Answers are generated based on the documentation.

Debug a kit

Availability: Early Access

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.

my-kit/my-kit.yaml
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 version

The 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.log

Read 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:

LogContents
/var/log/sbx-kits/<kit>/install-<n>.logFull 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.logOutput 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