> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boxlite.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Image Registry Configuration

> Configure BoxLite to pull OCI container images from custom registries, private enterprise registries, or local caching proxies.

This guide explains how to configure BoxLite to pull OCI container images from custom registries, such as a private enterprise registry, a third-party registry like `ghcr.io` or `quay.io`, or a local caching proxy.

## How it Works

When you ask BoxLite to create a box from an image (e.g., `image="alpine"`), it needs to resolve this "unqualified" reference into a full image reference (e.g., `docker.io/library/alpine:latest`).

By default, if no registries are configured, BoxLite uses `docker.io` (Docker Hub) as the implicit default.

You can provide a list of custom registries. BoxLite will try to pull the image from each registry in the provided order. The first successful pull wins. If all registries fail, the operation returns an error.

<Note>
  Fully qualified image references (e.g., `quay.io/prometheus/prometheus:v2.40.1`) always bypass the search mechanism and are pulled directly.
</Note>

***

## CLI Configuration

The CLI layers configuration sources with the following priority (from lowest to highest):

1. **Default**: `docker.io`
2. **Configuration File (`--config`)**: Loads configuration from the specified path
3. **CLI Flags (`--registry`)**: Prepended to registries from config file (highest priority)

### 1. Configuration File

Create a JSON configuration file with your registry preferences:

```json theme={null}
{
  "image_registries": [
    "ghcr.io",
    "quay.io",
    "docker.io"
  ]
}
```

The `image_registries` field is optional. It contains a list of registries to search for unqualified image references.

### 2. Using the Configuration File

Use the `--config` flag to specify your configuration file:

```bash theme={null}
# Use a project-specific configuration
boxlite --config ./project-config.json run alpine
```

<Warning>
  If you specify a config file with `--config` and the file does not exist or is invalid, the command will fail with an error.
</Warning>

### 3. Command Line Flags

You can use the global `--registry` flag with `boxlite run` or `boxlite create`. You can specify it multiple times.

These flags are **prepended** to your configured list. This allows you to force a specific registry to be checked first for a single command without editing your config file.

```bash theme={null}
# Assume config.json contains ["ghcr.io", "docker.io"]

# This command will search:
# 1. my.private.registry.com (from flag)
# 2. ghcr.io (from config)
# 3. docker.io (from config)
boxlite --config ./config.json \
  run --registry my.private.registry.com \
  my-internal-app:latest
```

***

## SDK Configuration

The SDKs are "pure" by design. They **do not** automatically load any configuration file. This ensures that your code's behavior is deterministic and does not silently depend on the user's local environment.

1. **Programmatic Options**: You explicitly pass the list of registries when initializing the runtime.
2. **Default**: `docker.io` (if you pass an empty list or nothing).

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import boxlite

    # Configure a runtime to search ghcr.io first, then docker.io
    options = boxlite.Options(
        image_registries=["ghcr.io", "docker.io"]
    )
    runtime = boxlite.Boxlite(options)

    # When creating a box, 'alpine' will be tried as:
    # 1. ghcr.io/library/alpine
    # 2. docker.io/library/alpine
    async with boxlite.SimpleBox(image="alpine", runtime=runtime) as box:
        await box.exec("echo", "Hello!")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { JsBoxlite, SimpleBox } from '@boxlite-ai/boxlite';

    // Configure a runtime to search ghcr.io first, then docker.io
    const runtime = new JsBoxlite({
      imageRegistries: ['ghcr.io', 'docker.io']
    });

    // Pass the custom runtime to the box constructor
    const box = new SimpleBox({
      image: 'alpine',
      runtime: runtime
    });

    await box.exec('echo', 'Hello!');
    ```
  </Tab>
</Tabs>

### Advanced: Loading Config in SDKs

If you want your SDK application to respect a configuration file, you can manually load it. This puts the control in your hands.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import boxlite
    import json
    from pathlib import Path

    def load_boxlite_options(config_path: str):
        """Load BoxLite options from a configuration file."""
        with open(config_path) as f:
            config = json.load(f)

        return boxlite.Options(
            image_registries=config.get("image_registries", [])
        )

    # Use it
    runtime = boxlite.Boxlite(load_boxlite_options("./config.json"))
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { readFileSync } from 'fs';
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    function loadBoxliteRuntime(configPath: string): JsBoxlite {
      const config = JSON.parse(readFileSync(configPath, 'utf-8'));
      return new JsBoxlite({
        imageRegistries: config.image_registries ?? [],
      });
    }

    // Use it
    const runtime = loadBoxliteRuntime('./config.json');
    ```
  </Tab>
</Tabs>

***

## Registry Resolution Order

To summarize the full resolution order for CLI usage:

<Steps>
  <Step title="Check if the image reference is fully qualified">
    If the image reference includes a registry domain (e.g., `quay.io/prometheus/prometheus:v2.40.1`), pull directly from that registry. Skip all other steps.
  </Step>

  <Step title="Try CLI --registry flags (highest priority)">
    If `--registry` flags were provided, try each one in order.
  </Step>

  <Step title="Try registries from configuration file">
    If a `--config` file was specified and it contains `image_registries`, try each one in order.
  </Step>

  <Step title="Fall back to docker.io">
    If no registries were configured, or all configured registries failed, try `docker.io` as the final fallback.
  </Step>
</Steps>

<Tip>
  For SDK usage, only steps 1, 3 (programmatic registries), and 4 apply. There are no CLI flags.
</Tip>

***

## Private Registry Authentication

BoxLite uses an OCI-compliant image puller that respects the system's Docker credential store. If your registry requires authentication, configure credentials through Docker's standard mechanism.

### Configure credentials

Use `docker login` to store credentials for your private registry:

```bash theme={null}
# Log in to a private registry
docker login my.private.registry.com

# Log in to GitHub Container Registry
docker login ghcr.io -u YOUR_GITHUB_USERNAME

# Log in to AWS ECR (requires AWS CLI)
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
```

BoxLite reads credentials from `~/.docker/config.json` at image pull time. No additional BoxLite-specific configuration is needed.

<Note>
  Credential helpers (e.g., `docker-credential-osxkeychain` on macOS, `docker-credential-ecr-login` for AWS) are supported. BoxLite invokes the same credential lookup chain that Docker uses.
</Note>

### Verify access

```bash theme={null}
# Test that BoxLite can pull from your private registry
boxlite run my.private.registry.com/my-image:latest echo "Success"
```

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Image not found (404)">
    Verify the image reference is correct and the image exists in the registry:

    ```bash theme={null}
    # Check with docker (or crane/skopeo) to confirm the image exists
    docker manifest inspect my.registry.com/my-image:latest
    ```

    Common causes:

    * Typo in the image name or tag
    * The image is in a different registry than expected (check your `image_registries` order)
    * The image was deleted or the tag was overwritten
  </Accordion>

  <Accordion title="Timeout pulling large images">
    Large images (multi-GB) can exceed default pull timeouts, especially on slow connections. BoxLite caches image layers, so subsequent pulls of the same or similar images are faster.

    Tips:

    * Use slim or alpine-based images where possible (e.g., `python:slim` instead of `python:latest`)
    * Pre-pull images before running time-sensitive workloads: `boxlite pull my-image:latest`
    * Use a local registry mirror or caching proxy to reduce network latency
  </Accordion>

  <Accordion title="TLS certificate errors">
    If your private registry uses a self-signed or corporate CA certificate:

    ```bash theme={null}
    # Add the CA certificate to the system trust store (macOS)
    sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.crt

    # Add the CA certificate to the system trust store (Ubuntu/Debian)
    sudo cp ca.crt /usr/local/share/ca-certificates/
    sudo update-ca-certificates
    ```

    BoxLite uses the system's TLS trust store for registry connections.
  </Accordion>

  <Accordion title="Authentication failed (401/403)">
    Verify your credentials are stored correctly:

    ```bash theme={null}
    # Check that credentials exist
    cat ~/.docker/config.json

    # Re-authenticate
    docker login my.private.registry.com
    ```

    If using a credential helper, ensure it is installed and working:

    ```bash theme={null}
    # Test the credential helper directly
    echo "my.private.registry.com" | docker-credential-osxkeychain get
    ```
  </Accordion>
</AccordionGroup>
