> ## Documentation Index
> Fetch the complete documentation index at: https://allhandsai-docs-custom-sandbox-images-restructure.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Building a Custom Sandbox Image

> How to build, version, and push a custom sandbox image for use with OpenHands Enterprise.

All custom sandbox image approaches — single-image Admin Console and multi-image
warm runtime pools — start here. Build your image once and then point whichever
configuration approach you use at it.

## Basic Pattern

1. Start from the OpenHands agent-server base image.
2. Keep the normal OpenHands entrypoint intact: extend the image, do not replace it.
3. Add your repo, docs, tools, and verification wrappers.
4. Pre-run the expensive setup you do not want to repeat at task time.
5. Push the image to a registry reachable from your OpenHands cluster.

<Warning>
  Do not override the entrypoint or replace the runtime contract of the base
  image. OpenHands expects standard agent-server behavior. Only extend, do not
  replace.
</Warning>

## Base Image

```dockerfile theme={null}
FROM ghcr.io/openhands/agent-server:1.46.0-python
```

Pin a specific version tag to ensure reproducible builds, and replace it with
the tag expected by your installed release. See [Version Compatibility](#version-compatibility)
below to find the right tag.

## Version Compatibility

Each OpenHands Enterprise release expects a specific agent-server version. The
base image tag you build from must match the release you run: the
`openhands-sdk` inside the sandbox and the one inside the OpenHands application
must agree on major and minor version.

To find the expected tag for your release, enable **Use a Custom Sandbox
Image** in the Admin Console. The **Sandbox Image Tag** field defaults to the
tag the current release expects. Check
[ghcr.io/openhands/agent-server](https://github.com/OpenHands/OpenHands/pkgs/container/agent-server)
for available tags.

When a conversation starts on a custom image, OpenHands checks the sandbox's
agent-server version. If it does not match, the conversation fails with an
error naming the expected and actual versions. Rebuild your image from the
expected tag to fix it.

<Note>
  Rebuild your custom image before each OHE upgrade. The agent-server base
  image changes with every release, and an image built for an older release
  will be rejected by the version check.
</Note>

## Build and Push

```bash theme={null}
docker buildx build \
  --platform linux/amd64 \
  -f your-project/Dockerfile \
  -t ghcr.io/<your-org>/openhands-custom-image:<your-tag> \
  --push \
  .
```

Use `--platform linux/amd64` because the Enterprise Replicated VM runs on x86-64.

## What to Bake In

Good candidates for prebaking:

* Pinned repository checkouts
* Package manager caches and installed dependencies (`node_modules`, Python virtualenvs, etc.)
* Compiled or transpiled output
* Native system packages (`xvfb`, `libkrb5-dev`, `pkg-config`, etc.)
* Browser or Electron artifacts
* Stable helper scripts such as `prepare-*` and `*-verify` wrappers

## What to Keep Out

<Warning>
  Do not bake the following into your image:

  * Secrets, API keys, or personal credentials
  * Machine-specific paths or environment assumptions
  * Uncommitted source changes or task-specific fixes
  * Rapidly changing dependencies (use a lightweight `prepare-*` script instead)
</Warning>

If the repository or dependencies change frequently, include a `prepare-*`
script in the image so the agent can refresh only the parts that need updating
without a full rebuild.

## Private Registries

If your image lives in a private registry, provide pull credentials so the
cluster can fetch it at pod start time.

**Replicated VM installs:** set **Registry Server**, **Registry Username**, and
**Registry Password or Credentials** in **Config → Sandbox Configuration** in
the Admin Console and deploy. The installer renders an image pull secret that
runtime pods automatically use.

**Helm installs:** create a pull secret in the `openhands` namespace and add
its name to the runtime-api `RUNTIME_IMAGE_PULL_SECRETS` environment variable
(comma-separated list of secret names).
